spree_cm_commissioner 2.13.4 → 2.13.7
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 +5 -0
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/admin/base_controller_decorator.rb +14 -0
- data/app/controllers/spree/admin/customer_notifications_controller.rb +16 -1
- data/app/controllers/spree/admin/inventory_items_controller.rb +11 -0
- data/app/controllers/spree/admin/payment_fee_rules_controller.rb +76 -0
- data/app/controllers/spree/admin/prepaid_transactions_controller.rb +69 -0
- data/app/controllers/spree/admin/private_sales_controller.rb +6 -0
- data/app/controllers/spree/admin/product_commissions_controller.rb +2 -1
- data/app/controllers/spree/admin/reschedule_policies_controller.rb +148 -0
- data/app/controllers/spree/admin/reschedule_requests_controller.rb +82 -0
- data/app/controllers/spree/admin/service_fee_rules_controller.rb +1 -0
- data/app/controllers/spree/admin/store_credit_admin_controller.rb +122 -0
- data/app/controllers/spree/admin/ticket_transfer_rules_controller.rb +7 -0
- data/app/controllers/spree/api/v2/platform/store_credits_controller_decorator.rb +35 -0
- data/app/controllers/spree/api/v2/storefront/account/prepaid_balances_controller.rb +52 -0
- data/app/controllers/spree/api/v2/storefront/account/prepaid_transactions_controller.rb +56 -0
- data/app/controllers/spree/api/v2/storefront/cart_payment_fees_controller.rb +48 -0
- data/app/controllers/spree/api/v2/storefront/draft_orders_controller.rb +37 -0
- data/app/controllers/spree/api/v2/storefront/prepaid_config_controller.rb +22 -0
- data/app/controllers/spree/api/v2/storefront/reschedule_requests_controller.rb +156 -0
- data/app/controllers/spree/api/v2/storefront/seat_layouts_controller.rb +3 -1
- data/app/controllers/spree/api/v2/storefront/transit/item_availabilities_controller.rb +66 -0
- data/app/controllers/spree/api/v2/tenant/account/prepaid_balances_controller.rb +51 -0
- data/app/controllers/spree/api/v2/tenant/account/prepaid_transactions_controller.rb +53 -0
- data/app/controllers/spree/api/v2/tenant/base_controller.rb +20 -0
- data/app/controllers/spree/api/v2/tenant/cart_payment_fees_controller.rb +43 -0
- data/app/controllers/spree/api/v2/tenant/draft_orders_controller.rb +42 -0
- data/app/controllers/spree/api/v2/tenant/prepaid_config_controller.rb +29 -0
- data/app/controllers/spree_cm_commissioner/admin/store_credits_controller_decorator.rb +217 -0
- data/app/errors/spree_cm_commissioner/reschedule_policies/invalid_policyable_error.rb +8 -0
- data/app/errors/spree_cm_commissioner/reschedule_policies/not_reschedulable_error.rb +11 -0
- data/app/errors/spree_cm_commissioner/reschedule_requests/cancel_not_allowed_error.rb +6 -0
- data/app/errors/spree_cm_commissioner/reschedule_requests/complete_cash_payment_error.rb +6 -0
- data/app/errors/spree_cm_commissioner/reschedule_requests/eligibility_denied_error.rb +7 -0
- data/app/factory/spree_cm_commissioner/order_telegram_message_factory.rb +6 -2
- data/app/factory/spree_cm_commissioner/reschedule_telegram_message_factory.rb +141 -0
- data/app/finders/spree_cm_commissioner/inventory_items/recently_changed_finder.rb +1 -1
- data/app/helpers/spree/admin/base_helper_decorator.rb +12 -0
- data/app/helpers/spree/admin/reschedule_policies_helper.rb +35 -0
- data/app/helpers/spree/admin/reschedule_requests_helper.rb +79 -0
- data/app/helpers/spree_cm_commissioner/admin/fee_rules_helper.rb +23 -0
- data/app/interactors/spree_cm_commissioner/create_ticket.rb +1 -0
- data/app/jobs/spree_cm_commissioner/audit_event_job.rb +29 -0
- data/app/jobs/spree_cm_commissioner/prepaid_reconciliation_cron_job.rb +9 -0
- data/app/jobs/spree_cm_commissioner/prepaid_reverse_top_up_job.rb +16 -0
- data/app/jobs/spree_cm_commissioner/prepaid_setup_types_job.rb +24 -0
- data/app/jobs/spree_cm_commissioner/prepaid_top_up_credit_job.rb +31 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/approval_reminder_job.rb +17 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/execute_job.rb +20 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/expire_overdue_requests_job.rb +12 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/expire_request_job.rb +15 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/payment_reminder_job.rb +17 -0
- data/app/models/concerns/spree_cm_commissioner/audit_wrapper.rb +139 -0
- data/app/models/concerns/spree_cm_commissioner/integrations/inventory_sync_cachable.rb +9 -0
- data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +2 -1
- data/app/models/concerns/spree_cm_commissioner/order_prepaid_payment.rb +15 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +58 -8
- data/app/models/concerns/spree_cm_commissioner/percent_fee_calculable.rb +36 -0
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +6 -2
- data/app/models/concerns/spree_cm_commissioner/reschedule_request_state_machine.rb +332 -0
- data/app/models/concerns/spree_cm_commissioner/scoped_fee_rule.rb +129 -0
- data/app/models/concerns/spree_cm_commissioner/service_type.rb +1 -1
- data/app/models/concerns/spree_cm_commissioner/store_preference.rb +1 -0
- data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +6 -0
- 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/core/controller_helpers/currency_decorator.rb +70 -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/payment_fee_flat_amount.rb +21 -0
- data/app/models/spree_cm_commissioner/calculators/payment_fee_percent.rb +17 -0
- data/app/models/spree_cm_commissioner/calculators/service_fee_flat_amount.rb +4 -0
- data/app/models/spree_cm_commissioner/calculators/service_fee_percent.rb +5 -2
- data/app/models/spree_cm_commissioner/configuration.rb +17 -9
- data/app/models/spree_cm_commissioner/currency_rate.rb +15 -0
- data/app/models/spree_cm_commissioner/current.rb +8 -0
- data/app/models/spree_cm_commissioner/customer_notification.rb +4 -1
- data/app/models/spree_cm_commissioner/exports/import_order_guests_csv.rb +153 -0
- data/app/models/spree_cm_commissioner/guest.rb +21 -0
- data/app/models/spree_cm_commissioner/integration.rb +31 -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/vireak_buntham.rb +82 -0
- data/app/models/spree_cm_commissioner/inventory_item.rb +22 -5
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +6 -2
- data/app/models/spree_cm_commissioner/notification.rb +8 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +31 -3
- data/app/models/spree_cm_commissioner/order_updater_decorator.rb +6 -1
- data/app/models/spree_cm_commissioner/payment_decorator.rb +31 -0
- data/app/models/spree_cm_commissioner/payment_fee_rule.rb +97 -0
- data/app/models/spree_cm_commissioner/payment_method_decorator.rb +5 -0
- data/app/models/spree_cm_commissioner/payment_method_store_credit_decorator.rb +81 -0
- data/app/models/spree_cm_commissioner/place.rb +2 -0
- data/app/models/spree_cm_commissioner/prepaid_config.rb +59 -0
- data/app/models/spree_cm_commissioner/prepaid_transaction.rb +102 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +6 -0
- data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +4 -2
- data/app/models/spree_cm_commissioner/reschedule_policy.rb +94 -0
- data/app/models/spree_cm_commissioner/reschedule_policy_assignment.rb +19 -0
- data/app/models/spree_cm_commissioner/reschedule_request.rb +227 -0
- data/app/models/spree_cm_commissioner/reschedule_requests/guest_selection.rb +80 -0
- data/app/models/spree_cm_commissioner/reschedule_snapshot.rb +65 -0
- data/app/models/spree_cm_commissioner/route.rb +1 -0
- data/app/models/spree_cm_commissioner/seat_layout.rb +8 -0
- data/app/models/spree_cm_commissioner/service_calendar.rb +4 -0
- data/app/models/spree_cm_commissioner/service_fee_rule.rb +17 -114
- 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/store_credit_category_decorator.rb +23 -0
- data/app/models/spree_cm_commissioner/store_credit_decorator.rb +142 -0
- data/app/models/spree_cm_commissioner/store_credit_event_decorator.rb +94 -0
- data/app/models/spree_cm_commissioner/store_credit_type_decorator.rb +43 -0
- data/app/models/spree_cm_commissioner/store_decorator.rb +8 -0
- data/app/models/spree_cm_commissioner/tenant.rb +37 -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 +79 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +9 -1
- 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 +53 -0
- data/app/models/spree_cm_commissioner/vendor_place.rb +2 -0
- data/app/notifications/spree_cm_commissioner/reschedule_approved_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_cancelled_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_completed_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_expired_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_failed_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_notification_base.rb +41 -0
- data/app/notifications/spree_cm_commissioner/reschedule_payment_reminder_notification.rb +15 -0
- data/app/notifications/spree_cm_commissioner/reschedule_rejected_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_requested_notification.rb +7 -0
- data/app/overrides/spree/admin/products/_form/offline.html.erb.deface +9 -0
- data/app/overrides/spree/admin/shared/_main_menu/sidebar_reschedule.html.erb.deface +9 -0
- data/app/overrides/spree/admin/shared/_main_menu/sidebar_store_credit.html.erb.deface +8 -0
- data/app/overrides/spree/admin/shared/_order_tabs/reschedule_requests.html.erb.deface +10 -0
- data/app/overrides/spree/admin/shared/_product_tabs/stock_managements.html.erb.deface +1 -1
- data/app/overrides/spree/admin/shared/sub_menu/_configuration/menu_items.html.erb.deface +5 -0
- data/app/presenters/spree_cm_commissioner/prepaid_balance_presenter.rb +16 -0
- data/app/queries/spree_cm_commissioner/store_credit_overview_query.rb +75 -0
- data/app/queries/spree_cm_commissioner/user_store_credit_summary_query.rb +53 -0
- data/app/serializables/spree_cm_commissioner/item_availability_sync.rb +10 -0
- data/app/serializers/spree/v2/storefront/prepaid_balance_serializer.rb +15 -0
- data/app/serializers/spree/v2/storefront/prepaid_config_serializer.rb +11 -0
- data/app/serializers/spree/v2/storefront/prepaid_transaction_serializer.rb +17 -0
- data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +6 -0
- data/app/serializers/spree/v2/tenant/cart_serializer.rb +2 -1
- data/app/serializers/spree/v2/tenant/payment_method_serializer.rb +3 -0
- data/app/serializers/spree/v2/tenant/prepaid_balance_serializer.rb +15 -0
- data/app/serializers/spree/v2/tenant/prepaid_config_serializer.rb +16 -0
- data/app/serializers/spree/v2/tenant/prepaid_transaction_serializer.rb +16 -0
- data/app/serializers/spree_cm_commissioner/api/v2/platform/homepage_section_relatable_options_serializer.rb +5 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/cart_serializer_decorator.rb +3 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/inventory_item_serializer.rb +1 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/item_availability_sync_serializer.rb +10 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/payment_fee_preview_serializer.rb +19 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/payment_method_serializer_decorator.rb +16 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/reschedule_request_serializer.rb +49 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/reschedule_snapshot_serializer.rb +24 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/seat_layout_serializer.rb +8 -0
- data/app/services/spree_cm_commissioner/audit_logger.rb +59 -0
- data/app/services/spree_cm_commissioner/checkout/add_store_credit_decorator.rb +17 -0
- data/app/services/spree_cm_commissioner/checkout/add_store_credit_payments.rb +96 -0
- data/app/services/spree_cm_commissioner/checkout/remove_store_credit_decorator.rb +17 -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/customer_notifications/clone.rb +39 -0
- data/app/services/spree_cm_commissioner/google_maps_geocoder.rb +41 -0
- data/app/services/spree_cm_commissioner/imports/orders/create.rb +24 -11
- 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/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_holds/release.rb +13 -0
- data/app/services/spree_cm_commissioner/inventory_items/generate_permanent_item.rb +48 -0
- data/app/services/spree_cm_commissioner/order_holds/hold.rb +17 -0
- data/app/services/spree_cm_commissioner/order_holds/release.rb +31 -0
- data/app/services/spree_cm_commissioner/payment_fees/apply.rb +30 -0
- data/app/services/spree_cm_commissioner/payment_fees/clear.rb +15 -0
- data/app/services/spree_cm_commissioner/payment_fees/preview.rb +70 -0
- data/app/services/spree_cm_commissioner/payments/find_or_create_decorator.rb +18 -0
- data/app/services/spree_cm_commissioner/place_resolver.rb +95 -0
- data/app/services/spree_cm_commissioner/prepaid/admin_credit.rb +44 -0
- data/app/services/spree_cm_commissioner/prepaid/check_top_up_limits.rb +47 -0
- data/app/services/spree_cm_commissioner/prepaid/freeze.rb +58 -0
- data/app/services/spree_cm_commissioner/prepaid/mutation.rb +78 -0
- data/app/services/spree_cm_commissioner/prepaid/reverse_order_top_up.rb +23 -0
- data/app/services/spree_cm_commissioner/prepaid/reverse_top_up.rb +53 -0
- data/app/services/spree_cm_commissioner/prepaid/top_up.rb +68 -0
- data/app/services/spree_cm_commissioner/prepaid/unfreeze.rb +47 -0
- data/app/services/spree_cm_commissioner/prepaid_metrics.rb +37 -0
- data/app/services/spree_cm_commissioner/prepaid_reconciliation_service.rb +71 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/assign.rb +52 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/create.rb +71 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/exclude.rb +40 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/resolve.rb +109 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/unassign.rb +23 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/update.rb +73 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/authorize_initiator.rb +79 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/build_request.rb +317 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/calculate_fee.rb +71 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/cancel.rb +55 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/check_eligibility.rb +243 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/collect_payment.rb +204 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/execute.rb +252 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/expire_overdue_requests.rb +28 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/expire_request.rb +24 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/guests/update_seats.rb +210 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/initiate.rb +255 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/issue_compensation.rb +67 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/issue_coupon.rb +90 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/issue_store_credit.rb +16 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_approval_reminder.rb +43 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_payment_reminder.rb +46 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_status_update.rb +42 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/seats/hold.rb +137 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/split_line_item.rb +86 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/telegram/notify_operations.rb +37 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/telegram/notify_vendor.rb +51 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/vendors/approve.rb +61 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/vendors/reject.rb +58 -0
- data/app/services/spree_cm_commissioner/topup_draft_orders/create.rb +234 -0
- data/app/services/spree_cm_commissioner/trips/service_calendars/create_or_update.rb +25 -14
- data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +43 -6
- data/app/views/spree/admin/classifications/edit.html.erb +17 -2
- data/app/views/spree/admin/classifications/new.html.erb +18 -2
- data/app/views/spree/admin/customer_notifications/index.html.erb +45 -3
- data/app/views/spree/admin/integrations/_book_me_bus_v1_fields.html.erb +47 -0
- data/app/views/spree/admin/integrations/_form.html.erb +12 -0
- data/app/views/spree/admin/integrations/_larryta_fields.html.erb +58 -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/payment_fee_rules/_form.html.erb +135 -0
- data/app/views/spree/admin/payment_fee_rules/edit.html.erb +11 -0
- data/app/views/spree/admin/payment_fee_rules/index.html.erb +100 -0
- data/app/views/spree/admin/payment_fee_rules/new.html.erb +11 -0
- data/app/views/spree/admin/prepaid_transactions/index.html.erb +102 -0
- data/app/views/spree/admin/prepaid_transactions/show.html.erb +146 -0
- data/app/views/spree/admin/private_sales/_form.html.erb +24 -1
- data/app/views/spree/admin/private_sales/_stock.html.erb +6 -1
- data/app/views/spree/admin/product_commissions/index.html.erb +7 -2
- data/app/views/spree/admin/reschedule_policies/_form.html.erb +65 -0
- data/app/views/spree/admin/reschedule_policies/edit.html.erb +11 -0
- data/app/views/spree/admin/reschedule_policies/index.html.erb +84 -0
- data/app/views/spree/admin/reschedule_policies/new.html.erb +11 -0
- data/app/views/spree/admin/reschedule_requests/_cancel_modal.html.erb +34 -0
- data/app/views/spree/admin/reschedule_requests/_search_form.html.erb +57 -0
- data/app/views/spree/admin/reschedule_requests/_tabs.html.erb +14 -0
- data/app/views/spree/admin/reschedule_requests/index.html.erb +64 -0
- data/app/views/spree/admin/reschedule_requests/show.html.erb +212 -0
- data/app/views/spree/admin/reschedule_requests/state_changes.html.erb +42 -0
- data/app/views/spree/admin/service_fee_rules/_form.html.erb +15 -9
- data/app/views/spree/admin/service_fee_rules/index.html.erb +1 -1
- data/app/views/spree/admin/shared/_products.html.erb +3 -0
- data/app/views/spree/admin/shared/_reschedule_policies_tabs.html.erb +22 -0
- data/app/views/spree/admin/shared/_ticket_transfer_details.html.erb +3 -0
- data/app/views/spree/admin/shared/sub_menu/_reschedule.html.erb +4 -0
- data/app/views/spree/admin/stock_managements/_header.html.erb +26 -0
- data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +1 -1
- data/app/views/spree/admin/stock_managements/calendar.html.erb +285 -171
- data/app/views/spree/admin/stock_managements/index.html.erb +1 -18
- data/app/views/spree/admin/store_credit_admin/_tabs.html.erb +31 -0
- data/app/views/spree/admin/store_credit_admin/_tenant_selector.html.erb +17 -0
- data/app/views/spree/admin/store_credit_admin/show.html.erb +200 -0
- data/app/views/spree/admin/store_credits/index.html.erb +224 -0
- data/app/views/spree/admin/tenants/_form.html.erb +33 -0
- data/app/views/spree/admin/ticket_transfer_rules/_form.html.erb +28 -5
- data/config/initializers/spree_dependencies.rb +3 -0
- data/config/initializers/spree_permitted_attributes.rb +1 -0
- data/config/locales/en.yml +363 -1
- data/config/locales/km.yml +282 -1
- data/config/routes.rb +67 -2
- 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/20260506120000_create_cm_reschedule_policies.rb +38 -0
- data/db/migrate/20260506120001_create_cm_reschedule_requests.rb +67 -0
- data/db/migrate/20260506120002_add_reschedule_count_to_cm_guests.rb +5 -0
- data/db/migrate/20260521140000_create_cm_reschedule_snapshots.rb +38 -0
- data/db/migrate/20260522160000_create_cm_reschedule_policy_assignments.rb +17 -0
- data/db/migrate/20260722000001_create_cm_prepaid_transactions.rb +31 -0
- data/db/migrate/20260722000002_add_constraints_to_store_credits.rb +46 -0
- data/db/migrate/20260722000003_add_expires_at_to_store_credits.rb +6 -0
- data/db/migrate/20260722000004_add_issuer_tenant_id_to_spree_store_credits.rb +7 -0
- data/db/migrate/20260722000005_add_immutability_trigger_to_cm_prepaid_transactions.rb +36 -0
- data/db/migrate/20260918045558_add_prepaid_currency_columns_to_cm_tenants.rb +6 -0
- data/db/migrate/20260923090000_add_public_private_metadata_to_cm_prepaid_transactions.rb +36 -0
- data/db/migrate/20260928000001_add_closed_to_cm_inventory_items.rb +5 -0
- data/db/migrate/20261001100000_create_cm_payment_fee_rules.rb +17 -0
- data/db/migrate/20261001100001_add_payment_fee_total_to_spree_orders.rb +5 -0
- data/db/migrate/20261006073317_add_offline_to_spree_products.rb +6 -0
- data/lib/spree_cm_commissioner/cached_inventory_item.rb +7 -1
- data/lib/spree_cm_commissioner/payment_fees/payment_fee_preview.rb +24 -0
- data/lib/spree_cm_commissioner/reschedule_policy_rules.rb +75 -0
- data/lib/spree_cm_commissioner/serializable_transaction.rb +38 -0
- data/lib/spree_cm_commissioner/store_credit_currency_summary.rb +11 -0
- data/lib/spree_cm_commissioner/store_credit_summary.rb +11 -0
- data/lib/spree_cm_commissioner/test_helper/factories/integration_factory.rb +11 -0
- data/lib/spree_cm_commissioner/test_helper/factories/prepaid_transaction_factory.rb +12 -0
- data/lib/spree_cm_commissioner/test_helper/factories/reschedule_policy_assignment_factory.rb +7 -0
- data/lib/spree_cm_commissioner/test_helper/factories/reschedule_policy_factory.rb +59 -0
- data/lib/spree_cm_commissioner/test_helper/factories/reschedule_request_factory.rb +51 -0
- data/lib/spree_cm_commissioner/test_helper/factories/reschedule_snapshot_factory.rb +27 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +5 -0
- data/lib/tasks/prepaid_reconciliation_cron_scheduler.rake +7 -0
- data/lib/tasks/prepaid_setup_types.rake +7 -0
- metadata +295 -2
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
module RescheduleRequestsHelper
|
|
4
|
+
STATE_BADGE_CLASSES = {
|
|
5
|
+
'pending_approval' => 'badge-warning',
|
|
6
|
+
'pending_payment' => 'badge-info',
|
|
7
|
+
'processing' => 'badge-primary',
|
|
8
|
+
'completed' => 'badge-success',
|
|
9
|
+
'rejected' => 'badge-danger',
|
|
10
|
+
'processing_failed' => 'badge-danger',
|
|
11
|
+
'cancelled' => 'badge-secondary',
|
|
12
|
+
'expired' => 'badge-dark'
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
# Drives Spree's always-visible quick-search box. Covers both identities:
|
|
16
|
+
# the account that booked (`user`, which the detail page labels Customer)
|
|
17
|
+
# and the contact details captured on the order, which are often
|
|
18
|
+
# different people and, for guest checkout, the only ones present.
|
|
19
|
+
SEARCH_PREDICATE = "#{%i[
|
|
20
|
+
number
|
|
21
|
+
order_number
|
|
22
|
+
order_email
|
|
23
|
+
order_phone_number
|
|
24
|
+
order_intel_phone_number
|
|
25
|
+
user_email
|
|
26
|
+
user_phone_number
|
|
27
|
+
].join('_or_')}_cont".freeze
|
|
28
|
+
|
|
29
|
+
def reschedule_request_search_predicate
|
|
30
|
+
SEARCH_PREDICATE
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def reschedule_request_state_badge(state)
|
|
34
|
+
klass = STATE_BADGE_CLASSES.fetch(state.to_s, 'badge-light')
|
|
35
|
+
content_tag :span, state.to_s.humanize, class: "badge #{klass}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# `approved` is a pseudo-status: `with_status` resolves it through
|
|
39
|
+
# `approved_at`, not `state`.
|
|
40
|
+
def reschedule_request_status_options
|
|
41
|
+
states = SpreeCmCommissioner::RescheduleRequest.state_machines[:state].states.map { |s| s.name.to_s }
|
|
42
|
+
(states + ['approved']).map { |status| [status.humanize, status] }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def reschedule_request_fee_label(request)
|
|
46
|
+
return "#{request.fee_percentage.to_d.round(2)}%" if request.fee_percentage.to_d.positive?
|
|
47
|
+
|
|
48
|
+
request.display_fee_amount
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def reschedule_snapshot_guests(snapshot)
|
|
52
|
+
return [] if snapshot.blank?
|
|
53
|
+
|
|
54
|
+
ids = SpreeCmCommissioner::RescheduleRequests::GuestSelection.snapshot_guest_ids(snapshot)
|
|
55
|
+
return [] if ids.blank?
|
|
56
|
+
|
|
57
|
+
SpreeCmCommissioner::Guest.where(id: ids).order(:id)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def reschedule_guest_label(guest)
|
|
61
|
+
guest.display_name_with_bib.presence || "Guest ##{guest.id}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def reschedule_compensation_link(request)
|
|
65
|
+
record = request.reschedule_compensation
|
|
66
|
+
return content_tag(:span, Spree.t(:none), class: 'text-muted') if record.blank?
|
|
67
|
+
|
|
68
|
+
case record
|
|
69
|
+
when Spree::Promotion
|
|
70
|
+
link_to record.name.presence || record.id, spree.edit_admin_promotion_path(record)
|
|
71
|
+
when Spree::StoreCredit
|
|
72
|
+
record.user_id ? link_to(record.display_amount, spree.admin_user_store_credit_path(record.user_id, record)) : record.display_amount
|
|
73
|
+
else
|
|
74
|
+
"#{record.class.name.demodulize.titleize} #{record.id}"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Admin
|
|
3
|
+
# Shared by the service fee and payment fee rule lists: the Scope column links to what the
|
|
4
|
+
# rule is scoped to (the rule itself is opened with the row's edit action).
|
|
5
|
+
module FeeRulesHelper
|
|
6
|
+
def fee_rule_scope_link(rule)
|
|
7
|
+
path = fee_rule_scope_path(rule)
|
|
8
|
+
path ? link_to(rule.scope_label, path) : rule.scope_label
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def fee_rule_scope_path(rule)
|
|
14
|
+
case rule.ruleable
|
|
15
|
+
when Spree::Product then spree.edit_admin_product_path(rule.ruleable)
|
|
16
|
+
when Spree::Taxon then spree.edit_admin_taxonomy_taxon_path(rule.ruleable.taxonomy_id, rule.ruleable.id)
|
|
17
|
+
when Spree::Vendor then spree.edit_admin_vendor_path(rule.ruleable)
|
|
18
|
+
when nil then rule.tenant ? spree.edit_admin_tenant_path(rule.tenant) : nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -136,6 +136,7 @@ module SpreeCmCommissioner
|
|
|
136
136
|
option_type_ids: context.params[:option_type_ids],
|
|
137
137
|
allow_anonymous_booking: context.params[:allow_anonymous_booking],
|
|
138
138
|
allow_self_check_in: context.params[:allow_self_check_in],
|
|
139
|
+
offline: context.params[:offline] || false,
|
|
139
140
|
purchasable_on: context.params[:purchasable_on] || 'both'
|
|
140
141
|
}
|
|
141
142
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class AuditEventJob < ApplicationUniqueJob
|
|
3
|
+
def perform(options = {})
|
|
4
|
+
SpreeCmCommissioner::AuditEvent.create!(
|
|
5
|
+
event_type: options[:event_type],
|
|
6
|
+
success: options.fetch(:success, true),
|
|
7
|
+
auditable: options[:auditable],
|
|
8
|
+
user: options[:user],
|
|
9
|
+
correlation_id: options[:correlation_id],
|
|
10
|
+
attempt: options.fetch(:attempt, 0),
|
|
11
|
+
metadata: options.fetch(:metadata, {}),
|
|
12
|
+
retention_until: options[:retention_until]
|
|
13
|
+
)
|
|
14
|
+
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e
|
|
15
|
+
# Log validation errors but don't retry - these are data issues, not transient failures
|
|
16
|
+
CmAppLogger.log(
|
|
17
|
+
label: 'AuditEventJob validation failed - non-retryable',
|
|
18
|
+
data: {
|
|
19
|
+
event_type: options[:event_type],
|
|
20
|
+
error_class: e.class.name,
|
|
21
|
+
error_message: e.message,
|
|
22
|
+
validation_errors: e.record&.errors&.full_messages
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
# Return nil to indicate the job completed (but audit wasn't saved)
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# Claws back a top-up's credit when its funding order is canceled -- nothing else in the
|
|
3
|
+
# codebase does this today, so without this job a canceled top-up order silently leaves the
|
|
4
|
+
# customer holding wallet balance nothing actually paid for.
|
|
5
|
+
# ApplicationUniqueJob (deduped by order_id): same reasoning as PrepaidTopUpCreditJob -- the
|
|
6
|
+
# ledger's idempotency_key already makes a duplicate run a safe no-op, this just skips wasting
|
|
7
|
+
# a SERIALIZABLE transaction attempt on it.
|
|
8
|
+
class PrepaidReverseTopUpJob < ApplicationUniqueJob
|
|
9
|
+
def perform(order_id:)
|
|
10
|
+
order = Spree::Order.find_by(id: order_id)
|
|
11
|
+
return if order.nil? || !order.canceled? || !order.prepaid_top_up_order?
|
|
12
|
+
|
|
13
|
+
SpreeCmCommissioner::Prepaid::ReverseOrderTopUp.call(order: order, reason: 'order canceled')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# One-time setup for the prepaid wallet's store-credit category, plus every
|
|
3
|
+
# Spree::StoreCreditType in STORE_CREDIT_TYPES (our own types and core's generic
|
|
4
|
+
# "Expiring"/"Non-expiring" -- harmless to seed those here too, find_or_create_by! is a no-op
|
|
5
|
+
# if core's own associate_credit_type already created them). Enqueue once per environment
|
|
6
|
+
# before enabling the feature (SpreeCmCommissioner::PrepaidSetupTypesJob.perform_later) --
|
|
7
|
+
# idempotent, so re-running it (retry, or by accident) is a no-op.
|
|
8
|
+
# ApplicationUniqueJob (not ApplicationJob) since it takes no arguments -- two concurrent runs
|
|
9
|
+
# could otherwise both miss on find_or_create_by!'s SELECT and race on the CREATE.
|
|
10
|
+
class PrepaidSetupTypesJob < ApplicationUniqueJob
|
|
11
|
+
def perform
|
|
12
|
+
category = Spree::StoreCreditCategory.wallet!
|
|
13
|
+
|
|
14
|
+
StoreCreditTypeDecorator::STORE_CREDIT_TYPES.each do |type_name|
|
|
15
|
+
Spree::StoreCreditType.find_or_create_by!(name: type_name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
CmAppLogger.log(
|
|
19
|
+
label: 'PrepaidSetupTypesJob completed',
|
|
20
|
+
data: { category: category.name, types: StoreCreditTypeDecorator::STORE_CREDIT_TYPES }
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# Credits the wallet for a prepaid_top_up order once the order completes.
|
|
3
|
+
# Deliberately does not wait for payment_fulfilled? -- pre-authorized payment methods
|
|
4
|
+
# (e.g. PayWay v2 with pre-auth) already hold the customer's funds at authorization
|
|
5
|
+
# time, and capture is done manually later -- see docs/concepts/store-credit-wallet-system.md
|
|
6
|
+
# Section 4.3.
|
|
7
|
+
# ApplicationUniqueJob (deduped by order_id): the ledger's idempotency_key already makes a
|
|
8
|
+
# duplicate run a safe no-op, this just skips wasting a SERIALIZABLE transaction attempt on it.
|
|
9
|
+
class PrepaidTopUpCreditJob < ApplicationUniqueJob
|
|
10
|
+
def perform(order_id:)
|
|
11
|
+
order = Spree::Order.find_by(id: order_id)
|
|
12
|
+
return if order.nil? || !order.complete? || !order.prepaid_top_up_order?
|
|
13
|
+
|
|
14
|
+
result = SpreeCmCommissioner::Prepaid::TopUp.call(
|
|
15
|
+
user: order.user,
|
|
16
|
+
amount: order.total,
|
|
17
|
+
currency: order.currency,
|
|
18
|
+
idempotency_key: "topup:order:#{order.number}",
|
|
19
|
+
issuer_tenant_id: order.tenant_id,
|
|
20
|
+
order: order
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return if result.success?
|
|
24
|
+
|
|
25
|
+
CmAppLogger.error(
|
|
26
|
+
label: 'PrepaidTopUpCreditJob#perform not credited',
|
|
27
|
+
data: { order: order.number, error: result.error.to_s }
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Per-request vendor decision nudge. Recipient: the VENDOR, over Telegram.
|
|
2
|
+
#
|
|
3
|
+
# Enqueued from the model's `after_commit :schedule_approval_reminder` with
|
|
4
|
+
# `wait_until:` set a fraction into the decision window. Thin wrapper - the state
|
|
5
|
+
# re-check and the idempotency stamp live in `SendApprovalReminder`.
|
|
6
|
+
module SpreeCmCommissioner
|
|
7
|
+
module RescheduleRequests
|
|
8
|
+
class ApprovalReminderJob < ApplicationJob
|
|
9
|
+
def perform(options = {})
|
|
10
|
+
request = SpreeCmCommissioner::RescheduleRequest.find_by(id: options[:reschedule_request_id])
|
|
11
|
+
return if request.nil?
|
|
12
|
+
|
|
13
|
+
SpreeCmCommissioner::RescheduleRequests::Notifications::SendApprovalReminder.call(reschedule_request: request)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Runs Execute once the balance-due payment is captured. Enqueued from the
|
|
2
|
+
# payment-completed hook so execution happens AFTER the payment commits — a
|
|
3
|
+
# failed Execute then lands in processing_failed without rolling back the money.
|
|
4
|
+
module SpreeCmCommissioner
|
|
5
|
+
module RescheduleRequests
|
|
6
|
+
class ExecuteJob < ApplicationJob
|
|
7
|
+
def perform(options = {})
|
|
8
|
+
request = SpreeCmCommissioner::RescheduleRequest.find_by(id: options[:reschedule_request_id])
|
|
9
|
+
return if request.nil?
|
|
10
|
+
return unless request.pending_payment? # idempotent: only the awaiting-payment request executes
|
|
11
|
+
|
|
12
|
+
# Authoritative paid check: the enqueuing hook fires mid-transaction,
|
|
13
|
+
# before payment_state settles. Nothing moves unpaid.
|
|
14
|
+
return unless request.order.payment_fulfilled?
|
|
15
|
+
|
|
16
|
+
SpreeCmCommissioner::RescheduleRequests::Execute.call(reschedule_request: request)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Cron safety net (every 15 minutes). Catches RescheduleRequests the
|
|
2
|
+
# per-request `ExpireRequestJob` missed (worker restart, queue loss).
|
|
3
|
+
# Thin wrapper — all iteration logic lives in `RescheduleRequests::ExpireOverdueRequests`.
|
|
4
|
+
module SpreeCmCommissioner
|
|
5
|
+
module RescheduleRequests
|
|
6
|
+
class ExpireOverdueRequestsJob < ApplicationJob
|
|
7
|
+
def perform
|
|
8
|
+
SpreeCmCommissioner::RescheduleRequests::ExpireOverdueRequests.call
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Per-request expiry. Enqueued from the model's `after_commit
|
|
2
|
+
# :schedule_expiration` with `wait_until: expires_at`. Thin wrapper that
|
|
3
|
+
# finds the request and hands off to `RescheduleRequests::ExpireRequest`.
|
|
4
|
+
module SpreeCmCommissioner
|
|
5
|
+
module RescheduleRequests
|
|
6
|
+
class ExpireRequestJob < ApplicationJob
|
|
7
|
+
def perform(options = {})
|
|
8
|
+
request = SpreeCmCommissioner::RescheduleRequest.find_by(id: options[:reschedule_request_id])
|
|
9
|
+
return if request.nil?
|
|
10
|
+
|
|
11
|
+
SpreeCmCommissioner::RescheduleRequests::ExpireRequest.call(reschedule_request: request)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Per-request payment nudge. Recipient: the CUSTOMER, over FCM plus the in-app list.
|
|
2
|
+
#
|
|
3
|
+
# Enqueued from the model's `after_commit :schedule_payment_reminder` with
|
|
4
|
+
# `wait_until:` set a fraction into the payment window. Thin wrapper - the state
|
|
5
|
+
# re-check and the idempotency stamp live in `SendPaymentReminder`.
|
|
6
|
+
module SpreeCmCommissioner
|
|
7
|
+
module RescheduleRequests
|
|
8
|
+
class PaymentReminderJob < ApplicationJob
|
|
9
|
+
def perform(options = {})
|
|
10
|
+
request = SpreeCmCommissioner::RescheduleRequest.find_by(id: options[:reschedule_request_id])
|
|
11
|
+
return if request.nil?
|
|
12
|
+
|
|
13
|
+
SpreeCmCommissioner::RescheduleRequests::Notifications::SendPaymentReminder.call(reschedule_request: request)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# AuditWrapper is a reusable mixin for classes that make external API calls.
|
|
3
|
+
#
|
|
4
|
+
# It wraps an API call in a three-event lifecycle:
|
|
5
|
+
# 1. <event_base>.request – logged before the call
|
|
6
|
+
# 2. <event_base>.response – logged after a successful call (includes timing)
|
|
7
|
+
# 3. <event_base>.failed – logged when the block raises, then re-raises
|
|
8
|
+
#
|
|
9
|
+
# Metadata follows this canonical shape:
|
|
10
|
+
# {
|
|
11
|
+
# request: { method:, endpoint:, body:, params: },
|
|
12
|
+
# response: { status:, body: },
|
|
13
|
+
# timing: { duration_ms: },
|
|
14
|
+
# error: nil | { message: }
|
|
15
|
+
# }
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
# include SpreeCmCommissioner::AuditWrapper
|
|
19
|
+
#
|
|
20
|
+
# with_audit(
|
|
21
|
+
# event_base: 'bookmebus.order.hold',
|
|
22
|
+
# request: { method: 'POST', endpoint: '/api/v2/trip_reservations', body: body },
|
|
23
|
+
# auditable: order,
|
|
24
|
+
# user: current_user
|
|
25
|
+
# ) do
|
|
26
|
+
# post("#{BASE_PATH}/trip_reservations", body)
|
|
27
|
+
# end
|
|
28
|
+
|
|
29
|
+
module AuditWrapper
|
|
30
|
+
extend ActiveSupport::Concern
|
|
31
|
+
|
|
32
|
+
def with_audit(event_base:, request: {}, auditable: nil, user: nil, # rubocop:disable Metrics/ParameterLists
|
|
33
|
+
attempt: 0, retention_days: nil
|
|
34
|
+
)
|
|
35
|
+
correlation_id = SecureRandom.uuid
|
|
36
|
+
|
|
37
|
+
shared = {
|
|
38
|
+
auditable: auditable,
|
|
39
|
+
user: user,
|
|
40
|
+
correlation_id: correlation_id,
|
|
41
|
+
attempt: attempt,
|
|
42
|
+
retention_days: retention_days
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# 1. Log the outbound request
|
|
46
|
+
AuditLogger.log!(
|
|
47
|
+
**shared,
|
|
48
|
+
event_type: "#{event_base}.request",
|
|
49
|
+
success: true,
|
|
50
|
+
metadata: build_request_metadata(request)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
54
|
+
result = yield
|
|
55
|
+
duration = elapsed_ms(started_at)
|
|
56
|
+
|
|
57
|
+
# 2. Log the successful response
|
|
58
|
+
AuditLogger.log!(
|
|
59
|
+
**shared,
|
|
60
|
+
event_type: "#{event_base}.response",
|
|
61
|
+
success: true,
|
|
62
|
+
metadata: build_response_metadata(request, result, duration)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
result
|
|
66
|
+
rescue StandardError => e
|
|
67
|
+
duration = elapsed_ms(started_at || Process.clock_gettime(Process::CLOCK_MONOTONIC))
|
|
68
|
+
|
|
69
|
+
# 3. Log the failure event
|
|
70
|
+
AuditLogger.log!(
|
|
71
|
+
**shared,
|
|
72
|
+
event_type: "#{event_base}.failed",
|
|
73
|
+
success: false,
|
|
74
|
+
metadata: build_error_metadata(request, e, duration)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
raise
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def build_request_metadata(request)
|
|
83
|
+
{
|
|
84
|
+
request: normalize_request(request),
|
|
85
|
+
response: nil,
|
|
86
|
+
timing: nil,
|
|
87
|
+
error: nil
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def build_response_metadata(request, result, duration_ms)
|
|
92
|
+
status, body = extract_response(result)
|
|
93
|
+
|
|
94
|
+
{
|
|
95
|
+
request: normalize_request(request),
|
|
96
|
+
response: { status: status, body: body },
|
|
97
|
+
timing: { duration_ms: duration_ms },
|
|
98
|
+
error: nil
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def build_error_metadata(request, error, duration_ms)
|
|
103
|
+
{
|
|
104
|
+
request: normalize_request(request),
|
|
105
|
+
response: nil,
|
|
106
|
+
timing: { duration_ms: duration_ms },
|
|
107
|
+
error: { message: error.message }
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def normalize_request(request)
|
|
112
|
+
{
|
|
113
|
+
method: request[:method],
|
|
114
|
+
endpoint: request[:endpoint],
|
|
115
|
+
body: request[:body],
|
|
116
|
+
params: request[:params]
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Try to extract status + body from common response objects.
|
|
121
|
+
# Falls back to inspecting the raw result.
|
|
122
|
+
def extract_response(result)
|
|
123
|
+
if result.respond_to?(:status) && result.respond_to?(:body)
|
|
124
|
+
[result.status, result.body]
|
|
125
|
+
elsif result.is_a?(Array)
|
|
126
|
+
# Handle arrays of resource objects
|
|
127
|
+
[nil, result.map(&:to_h)]
|
|
128
|
+
elsif result.respond_to?(:to_h)
|
|
129
|
+
[nil, result.to_h]
|
|
130
|
+
else
|
|
131
|
+
[nil, result.inspect]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def elapsed_ms(started_at)
|
|
136
|
+
((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at) * 1000).round(2)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module OrderPrepaidPayment
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
# Applies the buyer's eligible prepaid/coin balance (FEFO + issuer-scoped, Section 5.2 of
|
|
6
|
+
# docs/concepts/store-credit-wallet-system.md) to this order's outstanding balance. Goes
|
|
7
|
+
# through the injectable Spree::Dependencies point rather than referencing
|
|
8
|
+
# SpreeCmCommissioner::Checkout::AddStoreCreditPayments directly, so callers stay agnostic
|
|
9
|
+
# of the concrete service class.
|
|
10
|
+
def apply_prepaid_payment!(amount: nil)
|
|
11
|
+
result = Spree::Dependencies.checkout_add_store_credit_service.constantize.call(order: self, amount: amount)
|
|
12
|
+
result.success?
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -18,6 +18,10 @@ module SpreeCmCommissioner
|
|
|
18
18
|
# line item changes between steps.
|
|
19
19
|
state_machine.before_transition to: :payment, do: :create_service_fee_charge!
|
|
20
20
|
state_machine.before_transition from: :address, do: :create_service_fee_charge!
|
|
21
|
+
state_machine.before_transition to: :payment, do: :clear_stale_payment_fee!
|
|
22
|
+
state_machine.before_transition from: :address, do: :clear_stale_payment_fee!
|
|
23
|
+
|
|
24
|
+
state_machine.after_transition to: :payment, do: :apply_prepaid_payment_on_checkout, if: :user, unless: :skip_auto_apply_prepaid_payment?
|
|
21
25
|
|
|
22
26
|
# Fails fast, before any of the other before_transition/around_transition work below runs
|
|
23
27
|
# (external-system unstock, Redis unstock, notifications, ...), when the inventory hold has
|
|
@@ -29,6 +33,7 @@ module SpreeCmCommissioner
|
|
|
29
33
|
# purely an optimization so a doomed completion attempt doesn't first burn a call to the
|
|
30
34
|
# external system and a Redis round trip.
|
|
31
35
|
state_machine.before_transition to: :complete, do: :validate_inventory_hold_before_complete
|
|
36
|
+
|
|
32
37
|
state_machine.before_transition to: :complete, do: :request, if: :need_confirmation?
|
|
33
38
|
state_machine.before_transition to: :complete, do: :finalize_guests_and_bibs
|
|
34
39
|
state_machine.before_transition to: :complete, do: :generate_completion_steps!, if: :product_completion_steps_exist?
|
|
@@ -51,9 +56,11 @@ module SpreeCmCommissioner
|
|
|
51
56
|
state_machine.after_transition to: :canceled, do: :precalculate_conversion
|
|
52
57
|
state_machine.after_transition to: :canceled, do: :restock_inventory!
|
|
53
58
|
state_machine.after_transition to: :canceled, do: :de_allocate_resources
|
|
59
|
+
state_machine.after_transition to: :canceled, do: :enqueue_prepaid_top_up_reversal_job
|
|
54
60
|
|
|
55
61
|
state_machine.after_transition to: :complete, do: :increment_route_fulfilled_order_count
|
|
56
62
|
state_machine.after_transition to: :complete, do: :allocate_resources
|
|
63
|
+
state_machine.after_transition to: :complete, do: :enqueue_prepaid_top_up_credit_job
|
|
57
64
|
state_machine.after_transition to: :complete, do: :run_order_complete_alerts
|
|
58
65
|
|
|
59
66
|
scope :accepted, -> { where(request_state: 'accepted') }
|
|
@@ -100,6 +107,26 @@ module SpreeCmCommissioner
|
|
|
100
107
|
end
|
|
101
108
|
end
|
|
102
109
|
|
|
110
|
+
# Best-effort, like apply_prepaid_payment!'s other callers: a user with no (or no
|
|
111
|
+
# currency-matching) store credit just doesn't get a payment here, and the app can still
|
|
112
|
+
# remove/reapply it afterwards through the existing checkout store-credit endpoints.
|
|
113
|
+
def apply_prepaid_payment_on_checkout
|
|
114
|
+
apply_prepaid_payment!
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# subscription?: SubscriptionsOrderCreator already builds its own store-credit payment via
|
|
118
|
+
# create_default_payment_if_eligble, then drives the order through Spree::Checkout::Advance,
|
|
119
|
+
# which passes through :payment. Running this callback too would invalidate that payment
|
|
120
|
+
# mid-flow and rebuild a second one, racing the balance/capture logic already in progress.
|
|
121
|
+
#
|
|
122
|
+
# prepaid_top_up_order?: buying wallet balance with wallet balance is circular --
|
|
123
|
+
# Checkout::AddStoreCreditPayments already refuses these (see topup_paid_with_store_credit?),
|
|
124
|
+
# so this is belt-and-suspenders to keep the callback a visible no-op instead of a call that
|
|
125
|
+
# always fails deep in the service.
|
|
126
|
+
def skip_auto_apply_prepaid_payment?
|
|
127
|
+
subscription? || prepaid_top_up_order?
|
|
128
|
+
end
|
|
129
|
+
|
|
103
130
|
# override
|
|
104
131
|
def deliver_order_confirmation_email
|
|
105
132
|
return if requested?
|
|
@@ -156,18 +183,21 @@ module SpreeCmCommissioner
|
|
|
156
183
|
yield if block_given? # Equal to block.call
|
|
157
184
|
|
|
158
185
|
# After the transition is complete, the following code will execute first before proceeding to other `after_transition` callbacks.
|
|
159
|
-
# This ensures that if `reserve_order_holds
|
|
186
|
+
# This ensures that if `reserve_order_holds!`, `unstock_inventory_in_redis!`, or
|
|
187
|
+
# `capture_authorized_store_credit_payments!` fails, the state will be rolled back,
|
|
160
188
|
# and neither the `finalize!` method nor any notifications will be triggered.
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
|
|
189
|
+
# `unstock_inventory_from_external_system!` is the one exception: it calls out to a real
|
|
190
|
+
# external system, so a DB rollback here can't undo it, and any gateway-backed remainder
|
|
191
|
+
# payment already captured earlier (in process_payments!, before this transaction) can't be
|
|
192
|
+
# undone by this rollback either -- those still need vPago's own reversal/manual refund.
|
|
193
|
+
# Store credit is different: capture_authorized_store_credit_payments! is a plain DB write
|
|
194
|
+
# against our own tables, so it rolls back cleanly with everything else here -- no manual
|
|
195
|
+
# refund needed for that leg specifically.
|
|
196
|
+
|
|
197
|
+
capture_authorized_store_credit_payments!
|
|
164
198
|
unstock_inventory_from_external_system!
|
|
165
199
|
reserve_order_holds!
|
|
166
200
|
unstock_inventory_in_redis!
|
|
167
|
-
|
|
168
|
-
# We rollback only order state, and we keep payment state as it is.
|
|
169
|
-
# We implement payment in vPago gem, and it will be reversed in the gem.
|
|
170
|
-
# Some bank has api for refund, but some don't have the api to refund yet. So we keep the payment state as it is and refund manually.
|
|
171
201
|
end
|
|
172
202
|
rescue StandardError => e
|
|
173
203
|
CmAppLogger.log(
|
|
@@ -178,6 +208,16 @@ module SpreeCmCommissioner
|
|
|
178
208
|
raise e
|
|
179
209
|
end
|
|
180
210
|
|
|
211
|
+
# Store credit is authorized (not captured) at process_payments! time -- see
|
|
212
|
+
# Spree::PaymentMethod::StoreCredit#auto_capture, set false for this exact reason. Capturing
|
|
213
|
+
# runs first here, before stock is confirmed, but that's still safe: this whole method body is
|
|
214
|
+
# one transaction (see unstock_inventory! above), so if reserve_order_holds! or any later step
|
|
215
|
+
# fails, this capture rolls back along with it -- the customer is never left having spent
|
|
216
|
+
# store credit on an order that didn't actually complete.
|
|
217
|
+
def capture_authorized_store_credit_payments!
|
|
218
|
+
payments.valid.store_credits.where(state: :pending).find_each(&:capture!)
|
|
219
|
+
end
|
|
220
|
+
|
|
181
221
|
def restock_inventory!
|
|
182
222
|
ActiveRecord::Base.transaction do
|
|
183
223
|
restock_inventory_from_external_system!
|
|
@@ -389,6 +429,16 @@ module SpreeCmCommissioner
|
|
|
389
429
|
SpreeCmCommissioner::VotingCreditAllocationJob.perform_later(order_id: id)
|
|
390
430
|
end
|
|
391
431
|
|
|
432
|
+
def enqueue_prepaid_top_up_credit_job
|
|
433
|
+
SpreeCmCommissioner::PrepaidTopUpCreditJob.perform_later(order_id: id)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Same shape as enqueue_prepaid_top_up_credit_job above: always enqueue, the job itself
|
|
437
|
+
# re-checks prepaid_top_up_order? and no-ops for every other order.
|
|
438
|
+
def enqueue_prepaid_top_up_reversal_job
|
|
439
|
+
SpreeCmCommissioner::PrepaidReverseTopUpJob.perform_later(order_id: id)
|
|
440
|
+
end
|
|
441
|
+
|
|
392
442
|
def de_allocate_resources
|
|
393
443
|
SpreeCmCommissioner::VotingCreditDeAllocationJob.perform_later(order_id: id)
|
|
394
444
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# "10% + $0.25" fee shape shared by ServiceFeePercent and PaymentFeePercent. Lives outside
|
|
3
|
+
# SpreeCmCommissioner::Calculators on purpose: PricingAction.available_calculator_types lists
|
|
4
|
+
# every constant in that namespace, and a module there would show up as a pickable calculator
|
|
5
|
+
# type. The flat part is named like the *FlatAmount calculators' preference so admins see one
|
|
6
|
+
# term; each including calculator decides what it's multiplied by (per unit for service fees,
|
|
7
|
+
# once per payment for payment fees).
|
|
8
|
+
module PercentFeeCalculable
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
preference :percent, :decimal, default: 0
|
|
13
|
+
preference :flat_amount, :decimal, default: 0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def fee_description(currency:)
|
|
17
|
+
percent = "#{preferred_percent.to_d.round(2).to_s('F')}%"
|
|
18
|
+
return percent unless preferred_flat_amount.to_d.positive?
|
|
19
|
+
|
|
20
|
+
flat = Spree::Money.new(preferred_flat_amount, currency: currency).to_s
|
|
21
|
+
return flat if preferred_percent.to_d.zero?
|
|
22
|
+
|
|
23
|
+
Spree.t(:fee_percent_plus_flat, percent: percent, flat: flat)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
# Nothing to charge on a zero base (a free line item, or store credit covering the order),
|
|
29
|
+
# so the flat part isn't added on its own there either.
|
|
30
|
+
def percent_plus_flat(base, flat)
|
|
31
|
+
return 0 unless base.to_d.positive?
|
|
32
|
+
|
|
33
|
+
(base * preferred_percent / 100) + flat
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -5,9 +5,13 @@ module SpreeCmCommissioner
|
|
|
5
5
|
module ProductType
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
7
|
|
|
8
|
-
PRODUCT_TYPES = %i[accommodation service ecommerce transit show_episode vote_package advertisement].freeze
|
|
8
|
+
PRODUCT_TYPES = %i[accommodation service ecommerce transit show_episode vote_package advertisement prepaid_top_up].freeze
|
|
9
9
|
PERMANENT_STOCK_PRODUCT_TYPES = %w[accommodation service transit].freeze
|
|
10
|
-
PRE_INVENTORY_DAYS = {
|
|
10
|
+
PRE_INVENTORY_DAYS = {
|
|
11
|
+
'transit' => ENV.fetch('PRE_INVENTORY_DAYS_TRANSIT', 180).to_i,
|
|
12
|
+
'accommodation' => ENV.fetch('PRE_INVENTORY_DAYS_ACCOMMODATION', 365).to_i,
|
|
13
|
+
'service' => ENV.fetch('PRE_INVENTORY_DAYS_SERVICE', 180).to_i
|
|
14
|
+
}.freeze
|
|
11
15
|
|
|
12
16
|
included do
|
|
13
17
|
enum product_type: PRODUCT_TYPES if table_exists? && column_names.include?('product_type')
|