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,20 +2,26 @@ module Spree
|
|
|
2
2
|
module V2
|
|
3
3
|
module Storefront
|
|
4
4
|
module LineItemSerializerDecorator
|
|
5
|
-
def self.prepended(base)
|
|
5
|
+
def self.prepended(base) # rubocop:disable Metrics/MethodLength
|
|
6
6
|
base.attributes :from_date, :to_date, :need_confirmation, :product_type, :event_status, :amount, :display_amount,
|
|
7
7
|
:checkin_date, :checkout_date,
|
|
8
8
|
:number, :qr_data,
|
|
9
9
|
:kyc, :kyc_fields, :remaining_total_guests, :number_of_guests,
|
|
10
10
|
:completion_steps, :available_social_contact_platforms, :allow_anonymous_booking, :discontinue_on,
|
|
11
11
|
:high_demand, :jwt_token,
|
|
12
|
-
:direction, :trip_id, :boarding_trip_stop_id, :drop_off_trip_stop_id
|
|
12
|
+
:direction, :trip_id, :boarding_trip_stop_id, :drop_off_trip_stop_id,
|
|
13
|
+
:service_fee_total, :display_service_fee_total
|
|
13
14
|
|
|
14
15
|
base.attribute :required_self_check_in_location, &:required_self_check_in_location?
|
|
15
16
|
base.attribute :allowed_self_check_in, &:allowed_self_check_in?
|
|
16
17
|
base.attribute :allowed_upload_later, &:allowed_upload_later?
|
|
17
18
|
base.attribute :delivery_required, &:delivery_required?
|
|
18
19
|
base.attribute :sufficient_stock, &:sufficient_stock?
|
|
20
|
+
base.attribute :open_dated, &:open_dated?
|
|
21
|
+
base.attribute :redeemed, &:redeemed?
|
|
22
|
+
base.attribute :can_redeem, &:can_redeem?
|
|
23
|
+
base.attribute :expired, &:expired?
|
|
24
|
+
base.attribute :open_dated_route_label, &:open_dated_route_label
|
|
19
25
|
|
|
20
26
|
base.attribute :pickup_map_place do |line_item|
|
|
21
27
|
line_item.pickup_map_place&.to_h
|
|
@@ -13,7 +13,8 @@ module Spree
|
|
|
13
13
|
:promo_total, :display_promo_total, :item_count, :special_instructions, :display_total,
|
|
14
14
|
:pre_tax_item_amount, :display_pre_tax_item_amount, :pre_tax_total, :display_pre_tax_total,
|
|
15
15
|
:shipment_state, :payment_state, :public_metadata,
|
|
16
|
-
:phone_number, :intel_phone_number, :country_code, :request_state, :channel
|
|
16
|
+
:phone_number, :intel_phone_number, :country_code, :request_state, :channel,
|
|
17
|
+
:service_fee_total, :display_service_fee_total
|
|
17
18
|
|
|
18
19
|
belongs_to :user, serializer: Spree::V2::Tenant::UserSerializer
|
|
19
20
|
belongs_to :billing_address,
|
|
@@ -11,13 +11,19 @@ module Spree
|
|
|
11
11
|
:display_amount, :number, :qr_data, :kyc, :kyc_fields, :remaining_total_guests, :number_of_guests,
|
|
12
12
|
:completion_steps, :available_social_contact_platforms, :allow_anonymous_booking,
|
|
13
13
|
:discontinue_on, :high_demand, :jwt_token, :pre_tax_amount, :display_pre_tax_amount, :public_metadata,
|
|
14
|
-
:direction, :trip_id, :boarding_trip_stop_id, :drop_off_trip_stop_id, :passenger_count, :remark
|
|
14
|
+
:direction, :trip_id, :boarding_trip_stop_id, :drop_off_trip_stop_id, :passenger_count, :remark,
|
|
15
|
+
:service_fee_total, :display_service_fee_total
|
|
15
16
|
|
|
16
17
|
attribute :required_self_check_in_location, &:required_self_check_in_location?
|
|
17
18
|
attribute :allowed_self_check_in, &:allowed_self_check_in?
|
|
18
19
|
attribute :allowed_upload_later, &:allowed_upload_later?
|
|
19
20
|
attribute :delivery_required, &:delivery_required?
|
|
20
21
|
attribute :sufficient_stock, &:sufficient_stock?
|
|
22
|
+
attribute :open_dated, &:open_dated?
|
|
23
|
+
attribute :redeemed, &:redeemed?
|
|
24
|
+
attribute :can_redeem, &:can_redeem?
|
|
25
|
+
attribute :expired, &:expired?
|
|
26
|
+
attribute :open_dated_route_label, &:open_dated_route_label
|
|
21
27
|
|
|
22
28
|
attribute :pickup_map_place do |line_item|
|
|
23
29
|
line_item.pickup_map_place&.to_h
|
|
@@ -6,7 +6,8 @@ module SpreeCmCommissioner
|
|
|
6
6
|
|
|
7
7
|
attributes :first_name, :last_name, :dob, :gender, :kyc_fields, :other_occupation, :created_at, :updated_at,
|
|
8
8
|
:qr_data, :social_contact_platform, :social_contact, :available_social_contact_platforms, :event_id, :seat_number,
|
|
9
|
-
:age, :emergency_contact, :other_organization, :expectation, :upload_later, :address, :formatted_bib_number, :phone_number
|
|
9
|
+
:age, :emergency_contact, :other_organization, :expectation, :upload_later, :address, :formatted_bib_number, :phone_number,
|
|
10
|
+
:state
|
|
10
11
|
|
|
11
12
|
attribute :allowed_checkout, &:allowed_checkout?
|
|
12
13
|
attribute :require_kyc_field, &:require_kyc_field?
|
|
@@ -9,7 +9,8 @@ module SpreeCmCommissioner
|
|
|
9
9
|
|
|
10
10
|
base.attributes :phone_number, :intel_phone_number, :country_code, :request_state,
|
|
11
11
|
:term_accepted_at,
|
|
12
|
-
:channel, :hold_expires_at
|
|
12
|
+
:channel, :hold_expires_at,
|
|
13
|
+
:service_fee_total, :display_service_fee_total
|
|
13
14
|
|
|
14
15
|
base.attribute :has_incomplete_guest_info, &:has_incomplete_guest_info?
|
|
15
16
|
base.attribute :required_payway_payout, &:required_payway_payout?
|
|
@@ -148,7 +148,7 @@ module SpreeCmCommissioner
|
|
|
148
148
|
SpreeCmCommissioner::InvalidateCacheRequestJob.perform_later(patterns: patterns)
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
-
#
|
|
151
|
+
# Every key of BIT_SEGMENT (general, ticket, ..., ferry).
|
|
152
152
|
def homepage_segment
|
|
153
153
|
SpreeCmCommissioner::HomepageSectionBitwise::BIT_SEGMENT.keys
|
|
154
154
|
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require_dependency 'spree_cm_commissioner'
|
|
2
|
+
|
|
3
|
+
module SpreeCmCommissioner
|
|
4
|
+
# AuditLogger is a safe write-through service for API audit events.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# AuditLogger.log!(
|
|
8
|
+
# event_type: 'bookmebus.order.hold.request',
|
|
9
|
+
# success: true,
|
|
10
|
+
# metadata: { request: { method: 'POST', endpoint: '/api/v2/trip_reservations', body: {} } },
|
|
11
|
+
# actor: integration,
|
|
12
|
+
# auditable: order,
|
|
13
|
+
# user: current_user,
|
|
14
|
+
# correlation_id: 'uuid',
|
|
15
|
+
# attempt: 0,
|
|
16
|
+
# retention_days: 90
|
|
17
|
+
# )
|
|
18
|
+
#
|
|
19
|
+
# This service NEVER raises — any failure is swallowed and logged so it
|
|
20
|
+
# cannot disrupt the main application flow.
|
|
21
|
+
class AuditLogger
|
|
22
|
+
class << self
|
|
23
|
+
def log!( # rubocop:disable Metrics/ParameterLists
|
|
24
|
+
event_type:,
|
|
25
|
+
success: true,
|
|
26
|
+
metadata: {},
|
|
27
|
+
auditable: nil,
|
|
28
|
+
user: nil,
|
|
29
|
+
correlation_id: nil,
|
|
30
|
+
attempt: 0,
|
|
31
|
+
retention_days: nil
|
|
32
|
+
)
|
|
33
|
+
retention_until = (retention_days || 90).days.from_now.to_date
|
|
34
|
+
|
|
35
|
+
SpreeCmCommissioner::AuditEventJob.perform_later(
|
|
36
|
+
event_type: event_type,
|
|
37
|
+
success: success,
|
|
38
|
+
metadata: metadata,
|
|
39
|
+
auditable: auditable,
|
|
40
|
+
user: user,
|
|
41
|
+
correlation_id: correlation_id,
|
|
42
|
+
attempt: attempt,
|
|
43
|
+
retention_until: retention_until
|
|
44
|
+
)
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
CmAppLogger.error(
|
|
47
|
+
label: 'AuditLogger failed to persist audit event',
|
|
48
|
+
data: {
|
|
49
|
+
event_type: event_type,
|
|
50
|
+
error_class: e.class.name,
|
|
51
|
+
error_message: e.message,
|
|
52
|
+
backtrace: e.backtrace&.first(5)
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Checkout
|
|
3
|
+
# Same as Spree::Checkout::Advance, but stops at `payment` instead of running on to `confirm`.
|
|
4
|
+
# Lets a client reach the payment step in one call instead of stepping through `next` state by
|
|
5
|
+
# state -- useful wherever the checkout doesn't need per-state client input in between (e.g. a
|
|
6
|
+
# top-up order, or a booking flow where guest info is already assigned so there's nothing for
|
|
7
|
+
# the client to fill in at the address/delivery states).
|
|
8
|
+
# Fails with the order's error message when it can't get there.
|
|
9
|
+
class AdvanceToPayment
|
|
10
|
+
prepend Spree::ServiceModule::Base
|
|
11
|
+
|
|
12
|
+
def call(order:)
|
|
13
|
+
Spree::Dependencies.checkout_next_service.constantize.call(order: order) until cannot_make_transition?(order)
|
|
14
|
+
|
|
15
|
+
return success(order) if order.payment?
|
|
16
|
+
|
|
17
|
+
failure(order, order.errors.full_messages.to_sentence.presence)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def cannot_make_transition?(order)
|
|
23
|
+
order.payment? || order.confirm? || order.complete? || order.errors.present?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Verifies a token minted by Encode and resolves the user it was signed for. Two-step decode:
|
|
2
|
+
# peek at `sub` without verifying (the signing key is per-user, so the user must be looked up
|
|
3
|
+
# before the signature can be checked), then verify with that user's own secure_token.
|
|
4
|
+
#
|
|
5
|
+
# Also checks the token's `ver` against the user's current cross_app_handoff_version (see
|
|
6
|
+
# Encode) — a mismatch means either a newer token has since been minted (this one is stale) or
|
|
7
|
+
# this exact token was already consumed once (single-use). The check and the bump that follows a
|
|
8
|
+
# match happen inside the same row lock, so two concurrent replays of the same token can't both
|
|
9
|
+
# pass the check before either bumps it.
|
|
10
|
+
module SpreeCmCommissioner
|
|
11
|
+
module CrossAppHandoffTokens
|
|
12
|
+
class Decode
|
|
13
|
+
prepend Spree::ServiceModule::Base
|
|
14
|
+
|
|
15
|
+
def call(token:, purpose:)
|
|
16
|
+
unverified_payload = JWT.decode(token, nil, false).first
|
|
17
|
+
user = Spree.user_class.find_by(id: unverified_payload['sub'])
|
|
18
|
+
return failure(nil, 'invalid_token') if user.nil?
|
|
19
|
+
|
|
20
|
+
payload = JWT.decode(token, user.secure_token, true, algorithm: Encode::ALGORITHM).first
|
|
21
|
+
return failure(nil, 'invalid_purpose') unless payload['purpose'] == purpose.to_s
|
|
22
|
+
return failure(nil, 'invalid_token') unless consume_if_current_version!(user, payload['ver'])
|
|
23
|
+
|
|
24
|
+
success(user: user, payload: payload)
|
|
25
|
+
rescue JWT::DecodeError
|
|
26
|
+
failure(nil, 'invalid_token')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def consume_if_current_version!(user, token_version)
|
|
32
|
+
user.with_lock do
|
|
33
|
+
next false unless token_version == user.cross_app_handoff_version
|
|
34
|
+
|
|
35
|
+
user.cross_app_handoff_version += 1
|
|
36
|
+
user.save!
|
|
37
|
+
true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Mints a stateless, signed handoff token used to carry an authenticated user's identity into
|
|
2
|
+
# another app/session without a second login — modeled on Users::QrData (signed with the user's
|
|
3
|
+
# own secure_token, so it self-revokes if that column is regenerated) and LoadTest::LoginToken
|
|
4
|
+
# (short-lived JWT verified by signature + expiry alone, no DB row to look up or clean up).
|
|
5
|
+
#
|
|
6
|
+
# `purpose` is locked into the signed payload so a token minted for one hand-off can't be replayed
|
|
7
|
+
# for a different one, even before it expires — `operator_sign_in` (customer app → operator app)
|
|
8
|
+
# and `organizer_web_session` (operator app → organizer web dashboard) exist today. See Decode for
|
|
9
|
+
# the counterpart.
|
|
10
|
+
#
|
|
11
|
+
# `ver` (User#cross_app_handoff_version, a plain public_metadata counter — same shape as
|
|
12
|
+
# Users::QrData's qr_data_version, no migration needed) is bumped here on every mint, so minting a
|
|
13
|
+
# new token immediately invalidates any earlier, not-yet-consumed one for this user. Decode bumps
|
|
14
|
+
# it again on successful consumption, so the token just used can't be replayed either.
|
|
15
|
+
module SpreeCmCommissioner
|
|
16
|
+
module CrossAppHandoffTokens
|
|
17
|
+
class Encode
|
|
18
|
+
prepend Spree::ServiceModule::Base
|
|
19
|
+
|
|
20
|
+
TTL = 1.minute
|
|
21
|
+
ALGORITHM = 'HS256'.freeze
|
|
22
|
+
|
|
23
|
+
PURPOSES = %w[operator_sign_in organizer_web_session].freeze
|
|
24
|
+
|
|
25
|
+
def call(user:, purpose:)
|
|
26
|
+
return failure(nil, "invalid purpose: #{purpose}") unless PURPOSES.include?(purpose.to_s)
|
|
27
|
+
|
|
28
|
+
bump_version!(user)
|
|
29
|
+
|
|
30
|
+
expires_at = TTL.from_now
|
|
31
|
+
payload = { sub: user.id, purpose: purpose.to_s, ver: user.cross_app_handoff_version, exp: expires_at.to_i }
|
|
32
|
+
|
|
33
|
+
success(token: JWT.encode(payload, user.secure_token, ALGORITHM), expires_at: expires_at)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def bump_version!(user)
|
|
39
|
+
user.with_lock do
|
|
40
|
+
user.cross_app_handoff_version ||= 0
|
|
41
|
+
user.cross_app_handoff_version += 1
|
|
42
|
+
user.save!
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module CurrencyConverter
|
|
3
|
+
# Converts amounts between two currencies using vendor-specific exchange rates
|
|
4
|
+
class Create
|
|
5
|
+
attr_reader :vendor, :from_currency, :to_currency, :converted_amount
|
|
6
|
+
|
|
7
|
+
def initialize(vendor:, from_currency: 'USD', to_currency: 'USD')
|
|
8
|
+
@vendor = vendor
|
|
9
|
+
@from_currency = from_currency.upcase
|
|
10
|
+
@to_currency = to_currency.upcase
|
|
11
|
+
@converted_amount = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Converts the given amount from one currency to another
|
|
15
|
+
def call(amount)
|
|
16
|
+
return amount if from_currency == to_currency || amount.zero?
|
|
17
|
+
|
|
18
|
+
rate = vendor.exchange_rate(from_currency, to_currency)
|
|
19
|
+
raise MissingCurrencyRateError, "No conversion rate from #{from_currency} to #{to_currency} for vendor #{vendor.name}" if rate.nil?
|
|
20
|
+
|
|
21
|
+
@converted_amount = (amount * rate).round(2)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class MissingCurrencyRateError < StandardError; end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module CurrencyRates
|
|
3
|
+
# Manages currency rate creation and updates for vendors.
|
|
4
|
+
# Handles rate deduplication using floating-point tolerance and maintains
|
|
5
|
+
# a history of rate changes by marking old rates as inactive.
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# response = Manage.new(
|
|
9
|
+
# vendor_id: 1,
|
|
10
|
+
# from_currency: 'USD',
|
|
11
|
+
# to_currency: 'KHR',
|
|
12
|
+
# rate: 4100.50
|
|
13
|
+
# ).call
|
|
14
|
+
#
|
|
15
|
+
# if response.success?
|
|
16
|
+
# puts response.currency_rate.rate # => 4100.50
|
|
17
|
+
# else
|
|
18
|
+
# puts response.error # => "Rate must be greater than 0"
|
|
19
|
+
# end
|
|
20
|
+
class Manage
|
|
21
|
+
Response = Struct.new(:success?, :error, :currency_rate, keyword_init: true)
|
|
22
|
+
|
|
23
|
+
attr_reader :options, :vendor_id, :from_currency, :to_currency, :rate
|
|
24
|
+
|
|
25
|
+
def initialize(options = {})
|
|
26
|
+
@options = options
|
|
27
|
+
@vendor_id = options[:vendor_id]
|
|
28
|
+
@from_currency = options[:from_currency]
|
|
29
|
+
@to_currency = options[:to_currency]
|
|
30
|
+
@rate = options[:rate]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
return failure_response('Vendor ID is required') if vendor_id.blank?
|
|
35
|
+
return failure_response('From currency is required') if from_currency.blank?
|
|
36
|
+
return failure_response('To currency is required') if to_currency.blank?
|
|
37
|
+
return failure_response('Rate is required') if rate.blank?
|
|
38
|
+
return failure_response('Rate must be greater than 0') if rate.to_f <= 0
|
|
39
|
+
|
|
40
|
+
existing = existing_rates.first
|
|
41
|
+
|
|
42
|
+
# Deduplication: If a rate already exists with the same value (within floating-point tolerance),
|
|
43
|
+
# return the existing rate without creating a new one.
|
|
44
|
+
# This prevents duplicate entries when the same rate is submitted multiple times.
|
|
45
|
+
#
|
|
46
|
+
# Example:
|
|
47
|
+
# Existing rate: 4100.0
|
|
48
|
+
# New rate: 4100.00001 (difference: 0.00001, which is < 0.0001 tolerance)
|
|
49
|
+
# Result: Returns existing rate, no new record created
|
|
50
|
+
#
|
|
51
|
+
# The tolerance of 0.0001 accounts for floating-point precision issues
|
|
52
|
+
# and minor rounding differences in currency calculations.
|
|
53
|
+
return success_response(existing) if existing && (existing.rate - rate.to_f).abs < 0.0001
|
|
54
|
+
|
|
55
|
+
ActiveRecord::Base.transaction do
|
|
56
|
+
override_existing_rates!
|
|
57
|
+
new_rate = create_new_rate
|
|
58
|
+
success_response(new_rate)
|
|
59
|
+
end
|
|
60
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
61
|
+
failure_response(e.message)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def override_existing_rates!
|
|
67
|
+
existing_rates.each do |rate|
|
|
68
|
+
rate.update!(status: 'inactive', override_at: Time.current)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def existing_rates
|
|
73
|
+
vendor.currency_rates.by_pair(from_currency, to_currency).where(override_at: nil)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def vendor
|
|
77
|
+
@vendor ||= Spree::Vendor.find(vendor_id)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def create_new_rate
|
|
81
|
+
SpreeCmCommissioner::CurrencyRate.create!(
|
|
82
|
+
vendor_id: vendor_id,
|
|
83
|
+
from_currency: from_currency.upcase,
|
|
84
|
+
to_currency: to_currency.upcase,
|
|
85
|
+
rate: rate,
|
|
86
|
+
status: 'active'
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def success_response(currency_rate)
|
|
91
|
+
Response.new(success?: true, error: nil, currency_rate: currency_rate)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def failure_response(message)
|
|
95
|
+
Response.new(success?: false, error: message, currency_rate: nil)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class GoogleMapsGeocoder
|
|
3
|
+
class GeocodingError < StandardError; end
|
|
4
|
+
class ConfigurationError < StandardError; end
|
|
5
|
+
|
|
6
|
+
def initialize(api_key: nil)
|
|
7
|
+
@api_key = api_key || ENV.fetch('GOOGLE_MAP_KEY', nil)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def fetch_location_data(location_name, country_name)
|
|
11
|
+
raise ConfigurationError, 'Google Maps API key not configured' if @api_key.blank?
|
|
12
|
+
|
|
13
|
+
query = "#{location_name}, #{country_name}"
|
|
14
|
+
url = "#{GOOGLE_MAP_API_URL}/place/textsearch/json"
|
|
15
|
+
params = { query: query, key: @api_key }
|
|
16
|
+
|
|
17
|
+
response = Faraday.get(url, params)
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
data = JSON.parse(response.body)
|
|
21
|
+
rescue JSON::ParserError => e
|
|
22
|
+
raise GeocodingError, "Failed to parse Google Maps API response: #{e.message}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
unless data['status'] == 'OK' && data['results'].present?
|
|
26
|
+
error_msg = "Geocoding failed for '#{query}': #{data['status']}"
|
|
27
|
+
error_msg += " - #{data['error_message']}" if data['error_message'].present?
|
|
28
|
+
raise GeocodingError, error_msg
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
result = data['results'].first
|
|
32
|
+
location = result['geometry']['location']
|
|
33
|
+
|
|
34
|
+
{
|
|
35
|
+
lat: location['lat'],
|
|
36
|
+
lon: location['lng'],
|
|
37
|
+
reference: result['reference']
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -174,6 +174,17 @@ module SpreeCmCommissioner::Integrations::Base
|
|
|
174
174
|
@error.nil?
|
|
175
175
|
end
|
|
176
176
|
|
|
177
|
+
# Access metrics
|
|
178
|
+
# @return [Hash] The metrics hash
|
|
179
|
+
attr_reader :metrics
|
|
180
|
+
|
|
181
|
+
# Access API calls
|
|
182
|
+
# @return [Hash] The API calls hash
|
|
183
|
+
attr_reader :api_calls
|
|
184
|
+
|
|
185
|
+
# Timestamp of the last synced item (set by SyncItemAvailability)
|
|
186
|
+
attr_accessor :last_synced_at
|
|
187
|
+
|
|
177
188
|
# Serialize to hash for JSONB persistence
|
|
178
189
|
# @return [Hash] Serialized representation
|
|
179
190
|
def to_h
|