spree_cm_commissioner 2.13.2 → 2.13.3.pre.pre2
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/.env.example +1 -0
- data/Gemfile.lock +1 -1
- data/app/controllers/concerns/spree_cm_commissioner/operator/event_authorization.rb +19 -0
- data/app/controllers/spree/admin/homepage_section_controller.rb +32 -8
- data/app/controllers/spree/admin/integrations_controller.rb +5 -7
- data/app/controllers/spree/admin/inventory_items_controller.rb +12 -0
- data/app/controllers/spree/admin/notification_users_controller.rb +39 -7
- data/app/controllers/spree/admin/service_fee_rules_controller.rb +83 -0
- data/app/controllers/spree/admin/stock_managements_controller.rb +5 -5
- data/app/controllers/spree/api/switch_cm/base_controller.rb +98 -0
- data/app/controllers/spree/api/switch_cm/ota/avail_notifs_controller.rb +17 -0
- data/app/controllers/spree/api/switch_cm/ota/base_controller.rb +27 -0
- data/app/controllers/spree/api/switch_cm/ota/product_notifs_controller.rb +44 -0
- data/app/controllers/spree/api/switch_cm/ota/pushes_controller.rb +46 -0
- data/app/controllers/spree/api/switch_cm/ota/rate_amount_notifs_controller.rb +17 -0
- data/app/controllers/spree/api/switch_cm/ota/res_modify_notifs_controller.rb +31 -0
- data/app/controllers/spree/api/switch_cm/ota/res_notifs_controller.rb +29 -0
- data/app/controllers/spree/api/switch_cm/ota/reservation_streams_controller.rb +83 -0
- data/app/controllers/spree/api/switch_cm/tokens_controller.rb +68 -0
- data/app/controllers/spree/api/switch_cm/xml/availabilities_controller.rb +39 -0
- data/app/controllers/spree/api/switch_cm/xml/base_controller.rb +37 -0
- data/app/controllers/spree/api/switch_cm/xml/reservation_summaries_controller.rb +28 -0
- data/app/controllers/spree/api/switch_cm/xml/reservations_controller.rb +56 -0
- data/app/controllers/spree/api/switch_cm/xml/room_rate_availabilities_controller.rb +56 -0
- data/app/controllers/spree/api/switch_cm/xml/room_rates_controller.rb +32 -0
- data/app/controllers/spree/api/v2/operator/check_ins_controller.rb +7 -6
- data/app/controllers/spree/api/v2/operator/event_charts_controller.rb +8 -10
- data/app/controllers/spree/api/v2/operator/event_ticket_aggregators_controller.rb +14 -4
- data/app/controllers/spree/api/v2/operator/pie_chart_event_aggregators_controller.rb +15 -1
- data/app/controllers/spree/api/v2/platform/homepage_section_relatable_options_controller.rb +28 -0
- data/app/controllers/spree/api/v2/storefront/cross_app_handoff_tokens_controller.rb +41 -0
- data/app/controllers/spree/api/v2/storefront/homepage_sections_controller.rb +4 -2
- data/app/controllers/spree/api/v2/storefront/transit/item_availabilities_controller.rb +66 -0
- data/app/controllers/spree/api/v2/tenant/checkout_controller.rb +11 -0
- data/app/controllers/spree/api/v2/tenant/vendor_photos_controller.rb +25 -0
- data/app/controllers/spree_cm_commissioner/admin/orders_controller_decorator.rb +9 -1
- data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb +15 -0
- data/app/factory/spree_cm_commissioner/order_telegram_message_factory.rb +6 -2
- data/app/helpers/spree/admin/customer_notifications_helper.rb +57 -0
- data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +45 -23
- data/app/interactors/spree_cm_commissioner/customer_notification_sender.rb +18 -9
- data/app/interactors/spree_cm_commissioner/user_cross_app_handoff_authenticator.rb +17 -0
- data/app/jobs/spree_cm_commissioner/audit_event_job.rb +29 -0
- data/app/models/concerns/spree_cm_commissioner/audit_wrapper.rb +139 -0
- data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +37 -1
- data/app/models/concerns/spree_cm_commissioner/integrations/inventory_sync_cachable.rb +9 -0
- data/app/models/concerns/spree_cm_commissioner/line_item_open_dated_trippable.rb +2 -2
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +10 -0
- data/app/models/concerns/spree_cm_commissioner/phone_number_sanitizer.rb +5 -1
- data/app/models/concerns/spree_cm_commissioner/user_notification.rb +3 -1
- data/app/models/concerns/spree_cm_commissioner/variant_metadata.rb +24 -0
- data/app/models/concerns/spree_cm_commissioner/vehicle_kind.rb +1 -1
- data/app/models/spree_cm_commissioner/adjustable/adjuster/service_fee.rb +31 -0
- data/app/models/spree_cm_commissioner/adjustment_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/audit_event.rb +11 -0
- data/app/models/spree_cm_commissioner/calculators/service_fee_flat_amount.rb +17 -0
- data/app/models/spree_cm_commissioner/calculators/service_fee_percent.rb +20 -0
- data/app/models/spree_cm_commissioner/currency_rate.rb +15 -0
- data/app/models/spree_cm_commissioner/customer_notification.rb +15 -1
- data/app/models/spree_cm_commissioner/homepage_section.rb +25 -0
- data/app/models/spree_cm_commissioner/integration.rb +46 -0
- data/app/models/spree_cm_commissioner/integrations/book_me_bus_v1.rb +64 -0
- data/app/models/spree_cm_commissioner/integrations/larryta.rb +81 -0
- data/app/models/spree_cm_commissioner/integrations/stadium_x_v1.rb +4 -10
- data/app/models/spree_cm_commissioner/integrations/switch_cm.rb +86 -0
- data/app/models/spree_cm_commissioner/integrations/vireak_buntham.rb +82 -0
- data/app/models/spree_cm_commissioner/inventory_item.rb +18 -0
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +2 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +13 -1
- data/app/models/spree_cm_commissioner/order_updater_decorator.rb +31 -0
- data/app/models/spree_cm_commissioner/place.rb +2 -0
- data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +2 -1
- data/app/models/spree_cm_commissioner/route.rb +1 -0
- data/app/models/spree_cm_commissioner/seat_layout.rb +1 -0
- data/app/models/spree_cm_commissioner/service_fee_rule.rb +183 -0
- data/app/models/spree_cm_commissioner/stock/availability_checker.rb +1 -1
- data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +1 -1
- data/app/models/spree_cm_commissioner/stock_item_decorator.rb +4 -0
- data/app/models/spree_cm_commissioner/trip.rb +2 -1
- data/app/models/spree_cm_commissioner/trip_stop.rb +2 -0
- data/app/models/spree_cm_commissioner/user_decorator.rb +4 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +5 -0
- data/app/models/spree_cm_commissioner/vehicle.rb +3 -0
- data/app/models/spree_cm_commissioner/vehicle_type.rb +1 -0
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +17 -0
- data/app/models/spree_cm_commissioner/vendor_place.rb +2 -0
- data/app/overrides/spree/admin/orders/index/line_items_row.html.erb.deface +14 -0
- data/app/overrides/spree/admin/orders/index/row_actions.html.erb.deface +17 -0
- data/app/overrides/spree/admin/shared/sub_menu/_configuration/menu_items.html.erb.deface +5 -0
- data/app/queries/spree_cm_commissioner/users_by_event_fetcher_query.rb +6 -7
- data/app/serializables/spree_cm_commissioner/item_availability_sync.rb +10 -0
- data/app/serializers/spree/api/v2/platform/service_fee_rule_serializer.rb +17 -0
- data/app/serializers/spree/v2/storefront/homepage_section_serializer.rb +1 -1
- data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +8 -2
- data/app/serializers/spree/v2/tenant/cart_serializer.rb +2 -1
- data/app/serializers/spree/v2/tenant/line_item_serializer.rb +7 -1
- data/app/serializers/spree_cm_commissioner/v2/operator/guest_serializer.rb +2 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/cart_serializer_decorator.rb +2 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/cross_app_handoff_token_serializer.rb +9 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/item_availability_sync_serializer.rb +10 -0
- data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +1 -1
- data/app/services/spree_cm_commissioner/audit_logger.rb +59 -0
- data/app/services/spree_cm_commissioner/checkout/advance_to_payment.rb +27 -0
- data/app/services/spree_cm_commissioner/cross_app_handoff_tokens/decode.rb +42 -0
- data/app/services/spree_cm_commissioner/cross_app_handoff_tokens/encode.rb +47 -0
- data/app/services/spree_cm_commissioner/currency_converter/create.rb +27 -0
- data/app/services/spree_cm_commissioner/currency_rates/manage.rb +99 -0
- data/app/services/spree_cm_commissioner/draft_orders/create.rb +1 -0
- data/app/services/spree_cm_commissioner/google_maps_geocoder.rb +41 -0
- data/app/services/spree_cm_commissioner/integrations/base/sync_manager.rb +1 -2
- data/app/services/spree_cm_commissioner/integrations/base/sync_result.rb +11 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/external_client/client.rb +230 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/external_client/connection.rb +147 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/external_inventory_item_status.rb +52 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/external_inventory_items.rb +91 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/unstock_inventory.rb +188 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_inventory.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_item_availability.rb +354 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_locations.rb +103 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_nationality_pricing.rb +93 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_product.rb +70 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_route_stops.rb +121 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_routes.rb +128 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_seat_layout.rb +412 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_service_calendars.rb +59 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trip_stop.rb +224 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb +769 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_variant.rb +69 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_vehicle_types.rb +92 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/time_parser.rb +19 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/base.rb +40 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/inventory_item_status.rb +148 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/location.rb +55 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/reservation_cart.rb +85 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/seat.rb +128 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/seat_layout.rb +386 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/stop.rb +84 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/trip.rb +237 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/wallet_payment.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_manager.rb +22 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_strategies/full_sync_strategy.rb +116 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_strategies/incremental_sync_strategy.rb +63 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/external_client/client.rb +314 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/external_client/connection.rb +74 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/external_inventory_item_status.rb +53 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/external_inventory_items.rb +106 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/hold_seats.rb +143 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/release_seats.rb +90 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/unstock_inventory.rb +143 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_item_availability.rb +325 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_product.rb +72 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_routes.rb +142 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_seat_layout.rb +255 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_stock_item.rb +55 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_trip_stop.rb +171 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_trips.rb +397 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_variant.rb +63 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_vehicle_type.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/base.rb +39 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking.rb +113 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking_session.rb +43 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking_transaction.rb +236 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/direction.rb +71 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/passenger.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/schedule.rb +150 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat.rb +31 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat_availability.rb +49 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat_layout.rb +211 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/sync_manager.rb +17 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/sync_strategies/full_sync_strategy.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/sync_strategies/incremental_sync_strategy.rb +58 -0
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/external_client/client.rb +14 -15
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_matches.rb +7 -4
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_zones.rb +6 -3
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_manager.rb +0 -3
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/full_sync_strategy.rb +1 -3
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/incremental_sync_strategy.rb +1 -3
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/webhook_sync_strategy.rb +1 -2
- data/app/services/spree_cm_commissioner/integrations/switch_cm/accept_push.rb +101 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/accommodation_lock.rb +41 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/acknowledge_reservations.rb +43 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/apply_ari.rb +119 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/ari_update.rb +24 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/availability_readback.rb +86 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/mark_seen.rb +53 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/ota/avail_notif_parser.rb +60 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/ota/base_parser.rb +60 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/ota/push_response.rb +38 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/ota/rate_amount_notif_parser.rb +46 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/ota/reservation_presenter.rb +99 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/ota/response_builder.rb +52 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/pushed_nights.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/reservation.rb +118 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/reservation_feed.rb +143 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/room_catalog.rb +56 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/sync_manager.rb +16 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/sync_strategies/full_sync_strategy.rb +39 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/sync_strategies/incremental_sync_strategy.rb +51 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/unseen_room_nights.rb +39 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/validate_ari_updates.rb +136 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/xml/availability_parser.rb +103 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/xml/push_response.rb +45 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/xml/reservation_presenter.rb +74 -0
- data/app/services/spree_cm_commissioner/integrations/switch_cm/xml/response_builder.rb +19 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/external_client/client.rb +170 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/external_client/connection.rb +59 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/external_inventory_item_status.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/external_inventory_items.rb +92 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/unstock_inventory.rb +230 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_item_availability.rb +270 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_locations.rb +96 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_nationality_pricing.rb +76 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_product.rb +69 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_routes.rb +110 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_seat_layout.rb +207 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_stock_item.rb +43 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_trip_stop.rb +192 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_trips.rb +377 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_variant.rb +56 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_vehicle_type.rb +51 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/base.rb +32 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/booking.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/booking_transaction.rb +46 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/cell.rb +53 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/confirm_booking_request.rb +43 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/location.rb +37 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/schedule.rb +138 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/seat.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/seat_layout.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_manager.rb +17 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_strategies/full_sync_strategy.rb +87 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_strategies/incremental_sync_strategy.rb +53 -0
- data/app/services/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_by_variant.rb +4 -0
- data/app/services/spree_cm_commissioner/inventory_items/generate_permanent_item.rb +48 -0
- data/app/services/spree_cm_commissioner/inventory_items/reset.rb +4 -0
- data/app/services/spree_cm_commissioner/order_holds/hold.rb +17 -0
- data/app/services/spree_cm_commissioner/order_holds/release.rb +17 -0
- data/app/services/spree_cm_commissioner/place_resolver.rb +95 -0
- data/app/services/spree_cm_commissioner/transit_order/create.rb +1 -0
- data/app/services/spree_cm_commissioner/user_authenticator.rb +20 -12
- data/app/views/spree/admin/customer_notifications/_form.html.erb +9 -9
- data/app/views/spree/admin/customer_notifications/index.html.erb +36 -19
- data/app/views/spree/admin/homepage_section/_form.html.erb +19 -2
- data/app/views/spree/admin/homepage_section/_tabs.html.erb +7 -7
- data/app/views/spree/admin/homepage_section/index.html.erb +73 -6
- data/app/views/spree/admin/integrations/_book_me_bus_v1_fields.html.erb +47 -0
- data/app/views/spree/admin/integrations/_form.html.erb +16 -0
- data/app/views/spree/admin/integrations/_larryta_fields.html.erb +58 -0
- data/app/views/spree/admin/integrations/_switch_cm_fields.html.erb +53 -0
- data/app/views/spree/admin/integrations/_vireak_buntham_fields.html.erb +75 -0
- data/app/views/spree/admin/integrations/index.html.erb +1 -1
- data/app/views/spree/admin/notifications/index.html.erb +2 -1
- data/app/views/spree/admin/orders/line_items.html.erb +53 -0
- data/app/views/spree/admin/service_fee_rules/_form.html.erb +119 -0
- data/app/views/spree/admin/service_fee_rules/edit.html.erb +11 -0
- data/app/views/spree/admin/service_fee_rules/index.html.erb +91 -0
- data/app/views/spree/admin/service_fee_rules/new.html.erb +11 -0
- data/app/views/spree/admin/shared/_send_all_form.html.erb +1 -0
- data/app/views/spree/admin/shared/_send_test_form.html.erb +3 -3
- data/app/views/spree/admin/stock_managements/index.html.erb +14 -0
- data/config/locales/en.yml +36 -0
- data/config/locales/km.yml +32 -0
- data/config/routes.rb +34 -0
- data/db/migrate/20260327143200_create_cm_audit_events.rb +21 -0
- data/db/migrate/20260410045815_create_cm_currency_rates.rb +23 -0
- data/db/migrate/20260411102139_add_metadata_to_cm_inventory_items.rb +6 -0
- data/db/migrate/20260914000000_add_mini_app_to_homepage_sections.rb +5 -0
- data/db/migrate/20260917085551_create_cm_service_fee_rules.rb +16 -0
- data/db/migrate/20260917085552_add_service_fee_total_to_spree_line_items_and_orders.rb +6 -0
- data/db/migrate/20260928000000_add_oauth_application_id_to_cm_integrations.rb +5 -0
- data/db/migrate/20260928000001_add_closed_to_cm_inventory_items.rb +5 -0
- data/lib/spree_cm_commissioner/cached_inventory_item.rb +7 -1
- data/lib/spree_cm_commissioner/engine.rb +3 -0
- data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_factory.rb +7 -0
- data/lib/spree_cm_commissioner/test_helper/factories/integration_factory.rb +17 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +187 -4
|
@@ -2,10 +2,15 @@ module SpreeCmCommissioner
|
|
|
2
2
|
class InventoryItem < Base
|
|
3
3
|
include SpreeCmCommissioner::ProductType
|
|
4
4
|
include SpreeCmCommissioner::StoreMetadata
|
|
5
|
+
include SpreeCmCommissioner::Integrations::IntegrationMappable
|
|
5
6
|
|
|
6
7
|
MAX_DISPLAY_STOCK = 20
|
|
7
8
|
LOW_STOCK_THRESHOLD = 20
|
|
8
9
|
|
|
10
|
+
store_public_metadata :last_synced_at, :datetime
|
|
11
|
+
store_public_metadata :original_price, :integer
|
|
12
|
+
store_public_metadata :currency, :string
|
|
13
|
+
|
|
9
14
|
# Association
|
|
10
15
|
belongs_to :variant, class_name: 'Spree::Variant', optional: false
|
|
11
16
|
|
|
@@ -36,6 +41,11 @@ module SpreeCmCommissioner
|
|
|
36
41
|
|
|
37
42
|
before_save -> { self.product_type = variant.product_type }, if: -> { product_type.nil? }
|
|
38
43
|
|
|
44
|
+
# A night whose inventory a partner owns (Integration#inventory_external_wins?) starts closed: its
|
|
45
|
+
# count is only our seed from the physical room count, which the partner may already have sold
|
|
46
|
+
# elsewhere. The partner's first availability push opens it.
|
|
47
|
+
before_create -> { self.closed = true }, if: -> { permanent_stock? && variant&.inventory_external_wins? }
|
|
48
|
+
|
|
39
49
|
after_commit :schedule_product_cache_invalidation
|
|
40
50
|
after_commit :publish_live_stock_status_to_firestore
|
|
41
51
|
|
|
@@ -109,6 +119,14 @@ module SpreeCmCommissioner
|
|
|
109
119
|
# This method is only used when admin update stock
|
|
110
120
|
def adjust_quantity!(quantity)
|
|
111
121
|
with_lock do
|
|
122
|
+
# Warm the Redis cache from the current DB value BEFORE applying the delta below.
|
|
123
|
+
# adjust_quantity_in_redis treats a missing key as 0, so a negative delta against a
|
|
124
|
+
# never-cached key would compute e.g. 0 - 3 = -3, clamp to 0, and permanently cache the
|
|
125
|
+
# wrong value (the read path only self-heals from a genuinely *missing* key, not a wrong
|
|
126
|
+
# one). This is the same warm-before-delta pattern already used by
|
|
127
|
+
# InventoryItems::AdjustAvailableQuantity for the same reason.
|
|
128
|
+
SpreeCmCommissioner::RedisStock::CachedInventoryItemsBuilder.new([self]).call
|
|
129
|
+
|
|
112
130
|
# IMPORTANT: Apply quantity changes directly without defensive clamping.
|
|
113
131
|
# The model validation will catch any attempts to go negative, surfacing bugs
|
|
114
132
|
# in upstream logic rather than silently losing data.
|
|
@@ -3,6 +3,8 @@ module SpreeCmCommissioner
|
|
|
3
3
|
def self.prepended(base) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
4
4
|
include_modules(base)
|
|
5
5
|
|
|
6
|
+
base.money_methods :service_fee_total
|
|
7
|
+
|
|
6
8
|
base.belongs_to :accepter, class_name: 'Spree::User', optional: true
|
|
7
9
|
base.belongs_to :rejecter, class_name: 'Spree::User', optional: true
|
|
8
10
|
base.belongs_to :event, class_name: 'Spree::Taxon', optional: true, inverse_of: :line_items
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module OrderDecorator
|
|
3
|
-
def self.prepended(base) # rubocop:disable Metrics/MethodLength
|
|
3
|
+
def self.prepended(base) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
4
4
|
base.include SpreeCmCommissioner::OrderChannelBitwise
|
|
5
5
|
base.include SpreeCmCommissioner::StoreMetadata
|
|
6
6
|
base.include SpreeCmCommissioner::PhoneNumberSanitizer
|
|
@@ -11,6 +11,7 @@ module SpreeCmCommissioner
|
|
|
11
11
|
base.include SpreeCmCommissioner::OrderScopes
|
|
12
12
|
base.include SpreeCmCommissioner::TicketTransferable
|
|
13
13
|
base.include SpreeCmCommissioner::OrderQrCodeable
|
|
14
|
+
base.include SpreeCmCommissioner::Integrations::IntegrationMappable
|
|
14
15
|
|
|
15
16
|
base.before_create :link_by_phone_number
|
|
16
17
|
base.before_create :associate_customer
|
|
@@ -21,6 +22,8 @@ module SpreeCmCommissioner
|
|
|
21
22
|
base.store_private_metadata :preload_main_trip_ids, :array, default: []
|
|
22
23
|
base.store_public_metadata :order_options, :array, default: []
|
|
23
24
|
|
|
25
|
+
base.money_methods :service_fee_total
|
|
26
|
+
|
|
24
27
|
base.validates :promo_total, base::MONEY_VALIDATION
|
|
25
28
|
base.validates :phone_number, presence: true, if: :require_phone_number
|
|
26
29
|
|
|
@@ -49,6 +52,7 @@ module SpreeCmCommissioner
|
|
|
49
52
|
base.has_many :reserved_blocks, through: :guests, class_name: 'SpreeCmCommissioner::ReservedBlock'
|
|
50
53
|
base.has_many :guest_card_classes, class_name: 'SpreeCmCommissioner::GuestCardClass', through: :variants
|
|
51
54
|
base.has_many :product_completion_steps, class_name: 'SpreeCmCommissioner::ProductCompletionStep', through: :line_items
|
|
55
|
+
base.has_many :audit_events, class_name: 'SpreeCmCommissioner::AuditEvent', as: :auditable
|
|
52
56
|
|
|
53
57
|
base.delegate :customer, to: :user, allow_nil: true
|
|
54
58
|
|
|
@@ -156,6 +160,14 @@ module SpreeCmCommissioner
|
|
|
156
160
|
line_items.any?(&:delivery_required?)
|
|
157
161
|
end
|
|
158
162
|
|
|
163
|
+
# Mirrors create_tax_charge! (core/app/models/spree/order.rb) — wired into the same
|
|
164
|
+
# checkout transitions via SpreeCmCommissioner::OrderStateMachine, plus called directly
|
|
165
|
+
# from TransitOrder::Create and DraftOrders::Create, which build orders outside the
|
|
166
|
+
# checkout state machine entirely.
|
|
167
|
+
def create_service_fee_charge!
|
|
168
|
+
SpreeCmCommissioner::ServiceFeeRule.adjust(self, line_items)
|
|
169
|
+
end
|
|
170
|
+
|
|
159
171
|
# overrided
|
|
160
172
|
def payment_required?
|
|
161
173
|
return false if need_confirmation?
|
|
@@ -5,6 +5,37 @@ module SpreeCmCommissioner
|
|
|
5
5
|
order.item_total = line_items.sum(&:amount)
|
|
6
6
|
update_order_total
|
|
7
7
|
end
|
|
8
|
+
|
|
9
|
+
# Rolls line items' service_fee_total up into the order (display total only —
|
|
10
|
+
# the fee is already folded into order.adjustment_total/total via the
|
|
11
|
+
# non_taxable_adjustment_total bucket, see Adjustable::Adjuster::ServiceFee).
|
|
12
|
+
def update_adjustment_total
|
|
13
|
+
super
|
|
14
|
+
order.service_fee_total = line_items.sum(:service_fee_total)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Duplicates Spree::OrderUpdater#persist_totals' update_columns call (spree_core 4.5.3)
|
|
18
|
+
# rather than calling super + a second update_columns for service_fee_total, to keep this
|
|
19
|
+
# to a single UPDATE per order -- with read replicas/connection pooling in front of this,
|
|
20
|
+
# a second round-trip per persist_totals call isn't free. Must be kept in sync if core ever
|
|
21
|
+
# changes that column list.
|
|
22
|
+
def persist_totals
|
|
23
|
+
order.update_columns( # rubocop:disable Rails/SkipsModelValidations
|
|
24
|
+
payment_state: order.payment_state,
|
|
25
|
+
shipment_state: order.shipment_state,
|
|
26
|
+
item_total: order.item_total,
|
|
27
|
+
item_count: order.item_count,
|
|
28
|
+
adjustment_total: order.adjustment_total,
|
|
29
|
+
included_tax_total: order.included_tax_total,
|
|
30
|
+
additional_tax_total: order.additional_tax_total,
|
|
31
|
+
payment_total: order.payment_total,
|
|
32
|
+
shipment_total: order.shipment_total,
|
|
33
|
+
promo_total: order.promo_total,
|
|
34
|
+
total: order.total,
|
|
35
|
+
service_fee_total: order.service_fee_total,
|
|
36
|
+
updated_at: Time.current
|
|
37
|
+
)
|
|
38
|
+
end
|
|
8
39
|
end
|
|
9
40
|
end
|
|
10
41
|
|
|
@@ -49,7 +49,8 @@ module SpreeCmCommissioner
|
|
|
49
49
|
quantity_locked_on_hold: cache_inventory(locked_hold_keys[index], item, item.quantity_locked_on_hold, locked_hold[index]),
|
|
50
50
|
max_capacity: item.max_capacity,
|
|
51
51
|
inventory_item_id: item.id,
|
|
52
|
-
variant_id: item.variant_id
|
|
52
|
+
variant_id: item.variant_id,
|
|
53
|
+
closed: item.closed
|
|
53
54
|
)
|
|
54
55
|
end
|
|
55
56
|
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class ServiceFeeRule < SpreeCmCommissioner::Base
|
|
3
|
+
ALLOWED_RULEABLE_TYPES = %w[Spree::Product Spree::Taxon Spree::Vendor].freeze
|
|
4
|
+
|
|
5
|
+
include Spree::AdjustmentSource
|
|
6
|
+
include Spree::CalculatedAdjustments
|
|
7
|
+
|
|
8
|
+
# Spree::AdjustmentSource's own `has_many :adjustments, as: :source` has no class_name, so
|
|
9
|
+
# Rails infers it from the association name relative to *this* class's namespace
|
|
10
|
+
# (SpreeCmCommissioner::Adjustment), which doesn't exist. Redeclaring it here overrides that
|
|
11
|
+
# default — same fix SpreeCmCommissioner::PricingAction already needed for the same reason.
|
|
12
|
+
has_many :adjustments, as: :source, class_name: 'Spree::Adjustment'
|
|
13
|
+
|
|
14
|
+
belongs_to :ruleable, polymorphic: true, optional: true
|
|
15
|
+
|
|
16
|
+
# Only meaningful on a ruleable-less row (see the tenant_id absence validation below): a
|
|
17
|
+
# tenant's own default, distinct from the true platform-wide default (tenant_id AND ruleable
|
|
18
|
+
# both nil). Not used on Product/Vendor/Event-scoped rows — their tenant is already implied by
|
|
19
|
+
# the ruleable itself (a vendor belongs to exactly one tenant), so storing it there too would
|
|
20
|
+
# just be a second copy that could drift out of sync.
|
|
21
|
+
multi_tenant :tenant, class_name: 'SpreeCmCommissioner::Tenant'
|
|
22
|
+
|
|
23
|
+
before_validation :normalize_ruleable, :normalize_currency
|
|
24
|
+
|
|
25
|
+
validates :currency, presence: true
|
|
26
|
+
validates :ruleable_type, inclusion: { in: ALLOWED_RULEABLE_TYPES }, allow_nil: true
|
|
27
|
+
validates :ruleable_id, presence: true, if: -> { ruleable_type.present? }
|
|
28
|
+
validates :tenant_id, absence: true, if: -> { ruleable_type.present? }
|
|
29
|
+
|
|
30
|
+
# Only one active rule per (ruleable, tenant, currency). The global rule is a
|
|
31
|
+
# ruleable-less row, so ruleable_type / ruleable_id are both nil for it; tenant_id nil on
|
|
32
|
+
# that same row means "true platform-wide," not "unscoped."
|
|
33
|
+
validates :currency,
|
|
34
|
+
uniqueness: {
|
|
35
|
+
scope: %i[ruleable_type ruleable_id tenant_id active],
|
|
36
|
+
conditions: -> { where(active: true) },
|
|
37
|
+
case_sensitive: false,
|
|
38
|
+
message: lambda { |object, _data|
|
|
39
|
+
I18n.t('service_fee.rules.rule_already_exists_for_scope',
|
|
40
|
+
scope_type: object.ruleable_type_label, currency: object.currency
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
if: :active?
|
|
45
|
+
|
|
46
|
+
scope :active_rules, -> { where(active: true) }
|
|
47
|
+
scope :for_currency, -> (currency) { where(currency: currency.to_s.upcase) }
|
|
48
|
+
|
|
49
|
+
# Finds the active rule for the given ruleable (nil = global) and currency.
|
|
50
|
+
scope :active_for, lambda { |ruleable:, currency:|
|
|
51
|
+
active_rules
|
|
52
|
+
.for_currency(currency)
|
|
53
|
+
.where(ruleable_type: ruleable&.class&.base_class&.name, ruleable_id: ruleable&.id)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Finds the ruleable-less rule for a specific tenant (nil tenant_id = true platform-wide).
|
|
57
|
+
scope :active_for_tenant, lambda { |tenant_id:, currency:|
|
|
58
|
+
active_rules
|
|
59
|
+
.for_currency(currency)
|
|
60
|
+
.where(ruleable_type: nil, ruleable_id: nil, tenant_id: tenant_id)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Deletes all service-fee adjustments on these line items, then recreates them from
|
|
64
|
+
# whichever rule currently resolves for each one. Mirrors Spree::TaxRate.adjust: a plain
|
|
65
|
+
# destroy-then-create (not create_unique_adjustment, which only guards against duplicates
|
|
66
|
+
# and would leave a stale amount in place after a rule edit or a quantity change).
|
|
67
|
+
def self.adjust(order, line_items)
|
|
68
|
+
Spree::Adjustment.where(adjustable: line_items).service_fee.destroy_all
|
|
69
|
+
|
|
70
|
+
line_items.each do |line_item|
|
|
71
|
+
rule = resolve_for(line_item: line_item, currency: order.currency)
|
|
72
|
+
rule&.adjust(order, line_item)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# create_adjustment (from Spree::AdjustmentSource) is protected — callable with an explicit
|
|
77
|
+
# receiver only from inside another instance method of this class, not from the self.adjust
|
|
78
|
+
# class method above, where self is the class rather than an instance. This public wrapper is
|
|
79
|
+
# the same shape Spree::TaxRate#adjust uses for the identical reason.
|
|
80
|
+
def adjust(order, line_item)
|
|
81
|
+
create_adjustment(order, line_item)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Walks product -> event -> vendor -> global, returning the first active rule that matches the
|
|
85
|
+
# currency. The product/event/vendor leg mirrors
|
|
86
|
+
# SpreeCmCommissioner::TicketTransfers::FeeCalculator's precedence.
|
|
87
|
+
#
|
|
88
|
+
# "Global" means two different things depending on whether the vendor belongs to a tenant:
|
|
89
|
+
# - No tenant (a base BookMe+ vendor): the true platform-wide default (tenant_id nil).
|
|
90
|
+
# - Has a tenant: ONLY that tenant's own default (tenant_id: vendor.tenant_id) — deliberately
|
|
91
|
+
# does NOT fall back to the platform-wide default if the tenant hasn't set one. Each
|
|
92
|
+
# white-label tenant is expected to configure their own fee (or explicitly have none); a
|
|
93
|
+
# tenant silently inheriting BookMe+'s own fee because they forgot to configure one would be
|
|
94
|
+
# a surprising, not a sensible, default.
|
|
95
|
+
def self.resolve_for(line_item:, currency:)
|
|
96
|
+
ruleables_for(line_item).each do |ruleable|
|
|
97
|
+
rule = active_for(ruleable: ruleable, currency: currency).first
|
|
98
|
+
return rule if rule.present?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
active_for_tenant(tenant_id: vendor_for(line_item)&.tenant_id, currency: currency).first
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.ruleables_for(line_item)
|
|
105
|
+
[line_item.product, line_item.event, vendor_for(line_item)]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.vendor_for(line_item)
|
|
109
|
+
product = line_item.product
|
|
110
|
+
product.respond_to?(:vendor) ? product.vendor : nil
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Spree calculators only expose a fixed, hardcoded set of calculable model types
|
|
114
|
+
# (Rails.application.config.spree.calculators is a plain Struct — tax_rates,
|
|
115
|
+
# shipping_methods, promotion_actions_*). There's no slot for a commissioner-owned
|
|
116
|
+
# model, so — same as SpreeCmCommissioner::PricingAction — we expose our own list
|
|
117
|
+
# instead of relying on the inherited `calculators` class method.
|
|
118
|
+
def self.available_calculator_types
|
|
119
|
+
[SpreeCmCommissioner::Calculators::ServiceFeePercent, SpreeCmCommissioner::Calculators::ServiceFeeFlatAmount]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def compute_amount(line_item)
|
|
123
|
+
calculator&.compute(line_item) || 0
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# A short, human-readable summary for admin listings, e.g. "10%" or "$0.25" — so a rule's
|
|
127
|
+
# actual fee doesn't require opening it to see. Two calculator types (not one combined
|
|
128
|
+
# percent+flat calculator) means there's always exactly one number to show here.
|
|
129
|
+
def fee_description
|
|
130
|
+
case calculator
|
|
131
|
+
when SpreeCmCommissioner::Calculators::ServiceFeePercent
|
|
132
|
+
"#{calculator.preferred_percent.to_d.round(2).to_s('F')}%"
|
|
133
|
+
when SpreeCmCommissioner::Calculators::ServiceFeeFlatAmount
|
|
134
|
+
Spree::Money.new(calculator.preferred_flat_amount, currency: currency).to_s
|
|
135
|
+
when nil
|
|
136
|
+
Spree.t(:service_fee_rule_not_configured)
|
|
137
|
+
else
|
|
138
|
+
calculator.description
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def scope_label
|
|
143
|
+
if ruleable.blank?
|
|
144
|
+
# A tenant-scoped row is that tenant's own "global" — it never falls back to the
|
|
145
|
+
# BookMe+-level one (see resolve_for), so it isn't really "global" in the broad sense;
|
|
146
|
+
# labeled as its own Tenant scope rather than reusing the word "Global" for both.
|
|
147
|
+
return Spree.t(:service_fee_rule_scope_tenant, name: tenant&.name || "##{tenant_id}") if tenant_id.present?
|
|
148
|
+
|
|
149
|
+
return Spree.t(:service_fee_rule_scope_global)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
label_value = ruleable.respond_to?(:name) ? ruleable.name.to_s : "##{ruleable_id}"
|
|
153
|
+
"#{ruleable_type_label}: #{label_value}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def ruleable_type_label
|
|
157
|
+
case ruleable_type
|
|
158
|
+
when 'Spree::Product' then Spree.t(:service_fee_rule_scope_product)
|
|
159
|
+
when 'Spree::Taxon' then Spree.t(:service_fee_rule_scope_event)
|
|
160
|
+
when 'Spree::Vendor' then Spree.t(:service_fee_rule_scope_vendor)
|
|
161
|
+
else ruleable_type&.demodulize || Spree.t(:service_fee_rule_scope_global)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
protected
|
|
166
|
+
|
|
167
|
+
def label
|
|
168
|
+
'Service Fee'
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
private
|
|
172
|
+
|
|
173
|
+
# "Global" dropdown sends "" not nil. Convert to nil so validations and uniqueness checks work.
|
|
174
|
+
def normalize_ruleable
|
|
175
|
+
self.ruleable_type = nil if ruleable_type.blank?
|
|
176
|
+
self.ruleable_id = nil if ruleable_type.nil?
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def normalize_currency
|
|
180
|
+
self.currency = currency.to_s.upcase.presence
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -23,7 +23,7 @@ module SpreeCmCommissioner
|
|
|
23
23
|
return true if variant.need_confirmation?
|
|
24
24
|
return false if cached_inventory_items.empty?
|
|
25
25
|
|
|
26
|
-
cached_inventory_items.all? { |item| item.
|
|
26
|
+
cached_inventory_items.all? { |item| item.sellable? && quantity_in_pool(item, include_locked: include_locked) >= quantity }
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# The cache row carries both pools, so choosing between them is a read, not another Redis trip.
|
|
@@ -45,7 +45,7 @@ module SpreeCmCommissioner
|
|
|
45
45
|
# stock, i.e. read sold out exactly when its allotment was still sellable.
|
|
46
46
|
cached_inventory_items.all? do |item|
|
|
47
47
|
quantity = line_item.locked_stock? ? item.quantity_locked : item.quantity_available
|
|
48
|
-
item.
|
|
48
|
+
item.sellable? && quantity >= line_item.quantity
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
end
|
|
@@ -15,6 +15,10 @@ module SpreeCmCommissioner
|
|
|
15
15
|
private
|
|
16
16
|
|
|
17
17
|
def create_inventory_items
|
|
18
|
+
# Skip automatic inventory generation when variant has manual_generate_inventory_items set to true
|
|
19
|
+
# Use case: For variants where inventory items need to be manually created/managed (e.g., external integrations)
|
|
20
|
+
return if variant.manual_generate_inventory_items?
|
|
21
|
+
|
|
18
22
|
SpreeCmCommissioner::InventoryItems::GenerateInventoryItemsJob.perform_later(variant_id: variant.id)
|
|
19
23
|
end
|
|
20
24
|
|
|
@@ -2,6 +2,7 @@ module SpreeCmCommissioner
|
|
|
2
2
|
class Trip < Base
|
|
3
3
|
include SpreeCmCommissioner::StoreMetadata
|
|
4
4
|
include SpreeCmCommissioner::RouteType
|
|
5
|
+
include SpreeCmCommissioner::Integrations::IntegrationMappable
|
|
5
6
|
include SpreeCmCommissioner::ServiceOrigin
|
|
6
7
|
|
|
7
8
|
attr_accessor :hours, :minutes, :seconds
|
|
@@ -38,7 +39,7 @@ module SpreeCmCommissioner
|
|
|
38
39
|
after_commit :update_route_price_range, on: %i[create update]
|
|
39
40
|
|
|
40
41
|
belongs_to :vendor, class_name: 'Spree::Vendor', inverse_of: :trips, optional: false
|
|
41
|
-
belongs_to :product, class_name: 'Spree::Product', inverse_of: :trip, optional:
|
|
42
|
+
belongs_to :product, class_name: 'Spree::Product', inverse_of: :trip, optional: true
|
|
42
43
|
|
|
43
44
|
belongs_to :service_origin, class_name: 'Spree::Taxon', optional: true
|
|
44
45
|
|
|
@@ -59,6 +59,10 @@ module SpreeCmCommissioner
|
|
|
59
59
|
base.store_public_metadata :primary_service_type, :string
|
|
60
60
|
base.store_public_metadata :qr_data_version, :integer, default: 1
|
|
61
61
|
base.store_public_metadata :qr_data_invalidated_at, :datetime
|
|
62
|
+
# Bumped on both mint and consume by SpreeCmCommissioner::CrossAppHandoffTokens — minting
|
|
63
|
+
# invalidates any earlier, not-yet-consumed token; consuming invalidates the token just used,
|
|
64
|
+
# so a replay within its TTL fails. See CrossAppHandoffTokens::Encode/Decode.
|
|
65
|
+
base.store_public_metadata :cross_app_handoff_version, :integer, default: 0
|
|
62
66
|
base.store_public_metadata :branch_ids, :array, default: []
|
|
63
67
|
|
|
64
68
|
# Why the block reason lives in metadata rather than its own column: it is display-only text
|
|
@@ -6,6 +6,7 @@ module SpreeCmCommissioner
|
|
|
6
6
|
base.include SpreeCmCommissioner::VariantOptionsConcern
|
|
7
7
|
base.include SpreeCmCommissioner::KycBitwise
|
|
8
8
|
base.include SpreeCmCommissioner::Integrations::IntegrationMappable
|
|
9
|
+
base.include SpreeCmCommissioner::VariantMetadata
|
|
9
10
|
|
|
10
11
|
base.after_commit :update_vendor_price
|
|
11
12
|
base.validate :validate_option_types
|
|
@@ -80,6 +81,10 @@ module SpreeCmCommissioner
|
|
|
80
81
|
delivery_option == 'delivery'
|
|
81
82
|
end
|
|
82
83
|
|
|
84
|
+
def inventory_external_wins?
|
|
85
|
+
vendor&.integration&.inventory_external_wins?(self) || false
|
|
86
|
+
end
|
|
87
|
+
|
|
83
88
|
def non_digital_ecommerce?
|
|
84
89
|
!digital? && ecommerce?
|
|
85
90
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class Vehicle < Base
|
|
3
3
|
include SpreeCmCommissioner::VehicleKind
|
|
4
|
+
include SpreeCmCommissioner::Integrations::IntegrationMappable
|
|
4
5
|
|
|
5
6
|
belongs_to :vendor, class_name: 'Spree::Vendor'
|
|
6
7
|
belongs_to :vehicle_type, class_name: 'SpreeCmCommissioner::VehicleType'
|
|
@@ -13,6 +14,8 @@ module SpreeCmCommissioner
|
|
|
13
14
|
validates :name, presence: true
|
|
14
15
|
validates :license_plate, uniqueness: true, allow_blank: true
|
|
15
16
|
|
|
17
|
+
has_one :seat_layout, through: :vehicle_type
|
|
18
|
+
|
|
16
19
|
self.whitelisted_ransackable_attributes = %w[name license_plate kind]
|
|
17
20
|
|
|
18
21
|
def display_name
|
|
@@ -2,6 +2,7 @@ module SpreeCmCommissioner
|
|
|
2
2
|
class VehicleType < Base
|
|
3
3
|
include SpreeCmCommissioner::StoreMetadata
|
|
4
4
|
include SpreeCmCommissioner::VehicleKind
|
|
5
|
+
include SpreeCmCommissioner::Integrations::IntegrationMappable
|
|
5
6
|
|
|
6
7
|
# This model has no seat_layout column (polymorphic association), so we store the preload_seat_layout_id ID in public_metadata.
|
|
7
8
|
# This lets us check if a seat layout exists without triggering a database query.
|
|
@@ -33,6 +33,7 @@ module SpreeCmCommissioner
|
|
|
33
33
|
base.has_many :nearby_places, -> { order(position: :asc) }, class_name: 'SpreeCmCommissioner::VendorPlace', dependent: :destroy
|
|
34
34
|
|
|
35
35
|
base.has_many :stock_items, through: :variants, class_name: 'Spree::StockItem'
|
|
36
|
+
base.has_many :currency_rates, class_name: 'SpreeCmCommissioner::CurrencyRate', dependent: :destroy
|
|
36
37
|
|
|
37
38
|
base.has_many :taxon_vendors, class_name: 'SpreeCmCommissioner::TaxonVendor'
|
|
38
39
|
base.has_many :taxons, through: :taxon_vendors
|
|
@@ -104,6 +105,7 @@ module SpreeCmCommissioner
|
|
|
104
105
|
base.has_many :boarding_trip_stops, through: :trips, class_name: 'SpreeCmCommissioner::TripStop', source: :boarding_trip_stops
|
|
105
106
|
base.has_many :drop_off_trip_stops, through: :trips, class_name: 'SpreeCmCommissioner::TripStop', source: :drop_off_trip_stops
|
|
106
107
|
base.has_many :pricing_models, class_name: 'SpreeCmCommissioner::PricingModel', foreign_key: :vendor_id, dependent: :destroy
|
|
108
|
+
base.has_many :orders, -> { distinct }, through: :variants, source: :orders
|
|
107
109
|
|
|
108
110
|
base.has_many :agency_categories, -> { where(kind: :agency_category) }, class_name: 'Spree::Taxon', foreign_key: :vendor_id
|
|
109
111
|
|
|
@@ -248,6 +250,21 @@ module SpreeCmCommissioner
|
|
|
248
250
|
|
|
249
251
|
delegate :present?, to: :tenant, prefix: true
|
|
250
252
|
|
|
253
|
+
# Returns the exchange rate between two currencies
|
|
254
|
+
# If no direct rate exists, calculates the inverse rate from the reverse pair
|
|
255
|
+
# Returns 1.0 for identical currencies, nil if no rate is found
|
|
256
|
+
def exchange_rate(from_currency, to_currency)
|
|
257
|
+
return 1.0 if from_currency == to_currency
|
|
258
|
+
|
|
259
|
+
pair = currency_rates.by_pair(from_currency, to_currency).first
|
|
260
|
+
return pair.rate if pair.present?
|
|
261
|
+
|
|
262
|
+
reverse_pair = currency_rates.by_pair(to_currency, from_currency).first
|
|
263
|
+
return (1.0 / reverse_pair.rate).round(6) if reverse_pair.present?
|
|
264
|
+
|
|
265
|
+
nil
|
|
266
|
+
end
|
|
267
|
+
|
|
251
268
|
def validate_sellable_product_types
|
|
252
269
|
valid_types = SpreeCmCommissioner::ProductType::PRODUCT_TYPES.map(&:to_s)
|
|
253
270
|
return if sellable_product_types.all? { |type| valid_types.include?(type.to_s) }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- insert_after "[data-hook='admin_orders_index_rows']" -->
|
|
2
|
+
|
|
3
|
+
<tr>
|
|
4
|
+
<td colspan="100" class="p-0 border-top-0">
|
|
5
|
+
<div class="collapse" id="line-items-collapse-<%= order.id %>">
|
|
6
|
+
<div class="mb-3 overflow-hidden border rounded-lg">
|
|
7
|
+
<%= turbo_frame_tag "order_#{order.id}_line_items",
|
|
8
|
+
src: line_items_admin_order_path(order), loading: 'lazy' do %>
|
|
9
|
+
<div class="p-3 text-center text-muted"><%= Spree.t(:loading) %>…</div>
|
|
10
|
+
<% end %>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</td>
|
|
14
|
+
</tr>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- replace "[data-hook='admin_orders_index_row_actions']" -->
|
|
2
|
+
|
|
3
|
+
<td class='actions' data-hook="admin_orders_index_row_actions">
|
|
4
|
+
<span class="d-flex justify-content-end align-items-center">
|
|
5
|
+
<%= link_to_edit_url cart_admin_order_path(order), title: "admin_edit_#{dom_id(order)}", no_text: true if can?(:edit, order) %>
|
|
6
|
+
|
|
7
|
+
<%= link_to_with_icon 'credit-card.svg', Spree.t(:payments), admin_order_payments_path(order),
|
|
8
|
+
class: 'icon_link btn btn-light btn-sm ml-1', no_text: true if can?(:index, Spree::Payment) %>
|
|
9
|
+
|
|
10
|
+
<button type="button" data-toggle="collapse" data-target="#line-items-collapse-<%= order.id %>"
|
|
11
|
+
aria-expanded="false" aria-controls="line-items-collapse-<%= order.id %>"
|
|
12
|
+
class="pl-3 ml-1 btn btn-sm btn-light">
|
|
13
|
+
Line Items
|
|
14
|
+
<%= svg_icon(name: 'chevron-down.svg', classes: 'icon', width: 12, height: 12) %>
|
|
15
|
+
</button>
|
|
16
|
+
</span>
|
|
17
|
+
</td>
|
|
@@ -3,3 +3,8 @@
|
|
|
3
3
|
<%= configurations_sidebar_menu_item(Spree.t(:icons), spree.admin_vectors_icons_path) if can? :manage, Spree::Product %>
|
|
4
4
|
<%= configurations_sidebar_menu_item(Spree.t(:exports), spree.admin_exports_path) if can? :manage, SpreeCmCommissioner::Export %>
|
|
5
5
|
<%= configurations_sidebar_menu_item(Spree.t(:imports), spree.admin_imports_path) if can? :manage, SpreeCmCommissioner::Import %>
|
|
6
|
+
<%= configurations_sidebar_menu_item(
|
|
7
|
+
Spree.t(:service_fee_rules),
|
|
8
|
+
spree.admin_service_fee_rules_path(status: 'active'),
|
|
9
|
+
{ is_selected: controller.controller_name == 'service_fee_rules' }
|
|
10
|
+
) if can? :manage, SpreeCmCommissioner::ServiceFeeRule %>
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class UsersByEventFetcherQuery
|
|
3
|
-
attr_reader :
|
|
3
|
+
attr_reader :event_id
|
|
4
4
|
|
|
5
|
-
def initialize(
|
|
6
|
-
@
|
|
5
|
+
def initialize(event_id:)
|
|
6
|
+
@event_id = event_id
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
# Fetches distinct users with a completed order containing a line item for the given event(s)
|
|
9
10
|
def call
|
|
10
|
-
|
|
11
|
-
product_ids = classifications.pluck(:product_id)
|
|
12
|
-
Spree::User.joins(orders: { line_items: :variant })
|
|
11
|
+
Spree::User.joins(orders: :line_items)
|
|
13
12
|
.where(spree_orders: { state: 'complete' })
|
|
14
|
-
.where(
|
|
13
|
+
.where(spree_line_items: { event_id: @event_id })
|
|
15
14
|
.distinct
|
|
16
15
|
end
|
|
17
16
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Platform
|
|
5
|
+
class ServiceFeeRuleSerializer < BaseSerializer
|
|
6
|
+
attribute :type_name do |rule|
|
|
7
|
+
rule.class.name.underscore
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attribute :fee_description
|
|
11
|
+
|
|
12
|
+
has_one :calculator, if: proc { |rule| rule.respond_to?(:calculator) }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -2,7 +2,7 @@ module Spree
|
|
|
2
2
|
module V2
|
|
3
3
|
module Storefront
|
|
4
4
|
class HomepageSectionSerializer < BaseSerializer
|
|
5
|
-
attributes :id, :title, :segments, :description, :active, :position, :preview, :created_at, :updated_at
|
|
5
|
+
attributes :id, :title, :segments, :mini_apps, :description, :active, :position, :preview, :created_at, :updated_at
|
|
6
6
|
has_many :homepage_section_relatables
|
|
7
7
|
end
|
|
8
8
|
end
|