spree_cm_commissioner 2.13.4.pre.pre1 → 2.13.5
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/assets/config/spree_cm_commissioner_manifest.js +0 -1
- data/app/assets/javascripts/spree_cm_commissioner/backend.js +0 -8
- data/app/assets/stylesheets/spree_cm_commissioner/backend/commissioner_admin.css.scss +0 -1
- data/app/controllers/spree/admin/base_controller_decorator.rb +0 -12
- 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/service_fee_rules_controller.rb +1 -0
- data/app/controllers/spree/api/v2/storefront/cart_payment_fees_controller.rb +48 -0
- data/app/controllers/spree/api/v2/storefront/seat_layouts_controller.rb +3 -1
- data/app/controllers/spree/api/v2/tenant/cart_payment_fees_controller.rb +43 -0
- data/app/helpers/spree/{billing → admin}/navigation_helper_decorator.rb +2 -9
- data/app/helpers/spree_cm_commissioner/admin/fee_rules_helper.rb +23 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +2 -0
- data/app/models/concerns/spree_cm_commissioner/percent_fee_calculable.rb +36 -0
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +5 -1
- data/app/models/concerns/spree_cm_commissioner/scoped_fee_rule.rb +129 -0
- data/app/models/spree_cm_commissioner/adjustment_decorator.rb +1 -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/inventory_item.rb +10 -5
- data/app/models/spree_cm_commissioner/order_decorator.rb +12 -1
- data/app/models/spree_cm_commissioner/order_updater_decorator.rb +6 -1
- 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/redis_stock/cached_inventory_items_builder.rb +4 -2
- data/app/models/spree_cm_commissioner/seat_layout.rb +7 -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/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/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_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 +1 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/inventory_item_serializer.rb +1 -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/seat_layout_serializer.rb +8 -0
- data/app/services/spree_cm_commissioner/checkout/add_store_credit_decorator.rb +17 -0
- data/app/services/spree_cm_commissioner/checkout/remove_store_credit_decorator.rb +17 -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/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/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/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/config/locales/en.yml +12 -1
- data/config/locales/km.yml +12 -1
- data/config/routes.rb +4 -113
- 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/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/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +1 -0
- metadata +31 -231
- data/app/assets/javascripts/spree_cm_commissioner/address_hide_form.js +0 -17
- data/app/assets/javascripts/spree_cm_commissioner/location_checkbox_manager.js +0 -45
- data/app/assets/javascripts/spree_cm_commissioner/report_hide_filter.js +0 -37
- data/app/assets/javascripts/spree_cm_commissioner/role_form.js +0 -51
- data/app/assets/javascripts/spree_cm_commissioner/selected_field_filter_origin_destination.js +0 -37
- data/app/assets/javascripts/spree_cm_commissioner/trip_stop_selection.js +0 -41
- data/app/assets/javascripts/spree_cm_commissioner/update_popup_confirmation.js +0 -42
- data/app/assets/javascripts/spree_cm_commissioner/vehicle_seat.js +0 -291
- data/app/assets/stylesheets/spree_cm_commissioner/backend/invoice.scss +0 -92
- data/app/assets/stylesheets/spree_cm_commissioner/backend/vehicle_seat.scss +0 -28
- data/app/controllers/concerns/spree/billing/order_parents_concern.rb +0 -31
- data/app/controllers/concerns/spree/billing/payment_creatable.rb +0 -64
- data/app/controllers/concerns/spree/billing/payment_fireable.rb +0 -35
- data/app/controllers/concerns/spree_cm_commissioner/billing/role_authorization.rb +0 -53
- data/app/controllers/spree/billing/addresses_controller.rb +0 -32
- data/app/controllers/spree/billing/adjustments_controller.rb +0 -72
- data/app/controllers/spree/billing/base_controller.rb +0 -82
- data/app/controllers/spree/billing/businesses_controller.rb +0 -135
- data/app/controllers/spree/billing/customers_controller.rb +0 -107
- data/app/controllers/spree/billing/errors_controller.rb +0 -14
- data/app/controllers/spree/billing/invoice_controller.rb +0 -36
- data/app/controllers/spree/billing/option_types_controller.rb +0 -32
- data/app/controllers/spree/billing/orders_controller.rb +0 -90
- data/app/controllers/spree/billing/payment_qrcodes_controller.rb +0 -14
- data/app/controllers/spree/billing/payments_controller.rb +0 -148
- data/app/controllers/spree/billing/places_controller.rb +0 -35
- data/app/controllers/spree/billing/products_controller.rb +0 -56
- data/app/controllers/spree/billing/refunds_controller.rb +0 -9
- data/app/controllers/spree/billing/reports_controller.rb +0 -202
- data/app/controllers/spree/billing/roles_controller.rb +0 -107
- data/app/controllers/spree/billing/store_credits_controller.rb +0 -117
- data/app/controllers/spree/billing/subscriptions_controller.rb +0 -49
- data/app/controllers/spree/billing/users_controller.rb +0 -19
- data/app/controllers/spree/billing/variants_controller.rb +0 -36
- data/app/controllers/spree/billing/vendors_controller.rb +0 -83
- data/app/controllers/spree/transit/amenities_controller.rb +0 -79
- data/app/controllers/spree/transit/amenity_values_controller.rb +0 -16
- data/app/controllers/spree/transit/base_controller.rb +0 -53
- data/app/controllers/spree/transit/branches_controller.rb +0 -35
- data/app/controllers/spree/transit/locations_controller.rb +0 -38
- data/app/controllers/spree/transit/places_controller.rb +0 -151
- data/app/controllers/spree/transit/reservations_controller.rb +0 -15
- data/app/controllers/spree/transit/routes_controller.rb +0 -66
- data/app/controllers/spree/transit/service_calendars_controller.rb +0 -109
- data/app/controllers/spree/transit/stops_controller.rb +0 -32
- data/app/controllers/spree/transit/trip_stops_controller.rb +0 -35
- data/app/controllers/spree/transit/trips_controller.rb +0 -90
- data/app/controllers/spree/transit/vectors/amenity_values_controller.rb +0 -40
- data/app/controllers/spree/transit/vectors/icons_controller.rb +0 -26
- data/app/controllers/spree/transit/vehicle_photos_controller.rb +0 -52
- data/app/controllers/spree/transit/vehicle_seats_controller.rb +0 -29
- data/app/controllers/spree/transit/vehicle_types_controller.rb +0 -81
- data/app/controllers/spree/transit/vehicles_controller.rb +0 -54
- data/app/controllers/spree/transit/vendors_controller.rb +0 -83
- data/app/helpers/spree/billing/sortable_tree_helper.rb +0 -32
- data/app/helpers/spree/transit/navigation_helper_decorator.rb +0 -14
- data/app/helpers/spree/transit/sortable_tree_helper.rb +0 -32
- data/app/helpers/spree_cm_commissioner/billing/monthly_orders_helper.rb +0 -66
- data/app/helpers/spree_cm_commissioner/billing/qrcodes_helper.rb +0 -11
- data/app/helpers/spree_cm_commissioner/transit/service_calendars_helper.rb +0 -17
- data/app/views/spree/billing/addresses/_form.html.erb +0 -38
- data/app/views/spree/billing/addresses/index.html.erb +0 -19
- data/app/views/spree/billing/adjustments/_adjustment.html.erb +0 -34
- data/app/views/spree/billing/adjustments/_adjustments_table.html.erb +0 -32
- data/app/views/spree/billing/adjustments/_form.html.erb +0 -12
- data/app/views/spree/billing/adjustments/edit.html.erb +0 -13
- data/app/views/spree/billing/adjustments/index.html.erb +0 -32
- data/app/views/spree/billing/adjustments/new.html.erb +0 -9
- data/app/views/spree/billing/businesses/_assets_form.html.erb +0 -8
- data/app/views/spree/billing/businesses/_form.html.erb +0 -88
- data/app/views/spree/billing/businesses/edit.html.erb +0 -21
- data/app/views/spree/billing/businesses/index.html.erb +0 -32
- data/app/views/spree/billing/businesses/new.html.erb +0 -16
- data/app/views/spree/billing/customers/_form.html.erb +0 -44
- data/app/views/spree/billing/customers/edit.html.erb +0 -23
- data/app/views/spree/billing/customers/index.html.erb +0 -160
- data/app/views/spree/billing/customers/new.html.erb +0 -15
- data/app/views/spree/billing/errors/forbidden.html.erb +0 -7
- data/app/views/spree/billing/invoice/show.html.erb +0 -21
- data/app/views/spree/billing/option_types/_form.html.erb +0 -29
- data/app/views/spree/billing/option_types/_option_value_fields.html.erb +0 -13
- data/app/views/spree/billing/option_types/edit.html.erb +0 -56
- data/app/views/spree/billing/option_types/index.html.erb +0 -50
- data/app/views/spree/billing/option_types/new.html.erb +0 -13
- data/app/views/spree/billing/orders/_form.html.erb +0 -54
- data/app/views/spree/billing/orders/_line_item.html.erb +0 -57
- data/app/views/spree/billing/orders/_search.html.erb +0 -62
- data/app/views/spree/billing/orders/edit.html.erb +0 -16
- data/app/views/spree/billing/orders/index.html.erb +0 -84
- data/app/views/spree/billing/orders/show.html.erb +0 -1
- data/app/views/spree/billing/payments/_list.html.erb +0 -49
- data/app/views/spree/billing/payments/_state_changes.html.erb +0 -33
- data/app/views/spree/billing/payments/index.html.erb +0 -35
- data/app/views/spree/billing/payments/new.html.erb +0 -24
- data/app/views/spree/billing/payments/show.html.erb +0 -15
- data/app/views/spree/billing/places/_form.html.erb +0 -16
- data/app/views/spree/billing/places/edit.html.erb +0 -12
- data/app/views/spree/billing/places/index.html.erb +0 -72
- data/app/views/spree/billing/places/new.html.erb +0 -15
- data/app/views/spree/billing/products/_add_stock_form.html.erb +0 -40
- data/app/views/spree/billing/products/_filters.html.erb +0 -31
- data/app/views/spree/billing/products/_form.html.erb +0 -123
- data/app/views/spree/billing/products/edit.html.erb +0 -8
- data/app/views/spree/billing/products/index.html.erb +0 -72
- data/app/views/spree/billing/products/new.html.erb +0 -85
- data/app/views/spree/billing/products/stock.html.erb +0 -77
- data/app/views/spree/billing/reports/_date_range_picker.html.erb +0 -78
- data/app/views/spree/billing/reports/_overview.html.erb +0 -48
- data/app/views/spree/billing/reports/active_subscribers.html.erb +0 -97
- data/app/views/spree/billing/reports/balance_due.html.erb +0 -188
- data/app/views/spree/billing/reports/export.html.erb +0 -70
- data/app/views/spree/billing/reports/failed_orders.html.erb +0 -58
- data/app/views/spree/billing/reports/index.html.erb +0 -62
- data/app/views/spree/billing/reports/overdue.html.erb +0 -61
- data/app/views/spree/billing/reports/paid.html.erb +0 -60
- data/app/views/spree/billing/roles/_form.html.erb +0 -52
- data/app/views/spree/billing/roles/edit.html.erb +0 -13
- data/app/views/spree/billing/roles/index.html.erb +0 -44
- data/app/views/spree/billing/roles/new.html.erb +0 -13
- data/app/views/spree/billing/shared/_customer_credit_form.html.erb +0 -33
- data/app/views/spree/billing/shared/_customer_promotion_form.html.erb +0 -29
- data/app/views/spree/billing/shared/_customer_promotion_table.html.erb +0 -39
- data/app/views/spree/billing/shared/_customer_tabs.html.erb +0 -46
- data/app/views/spree/billing/shared/_header.html.erb +0 -21
- data/app/views/spree/billing/shared/_invoice.html.erb +0 -280
- data/app/views/spree/billing/shared/_language_switcher.html.erb +0 -5
- data/app/views/spree/billing/shared/_main_menu.html.erb +0 -73
- data/app/views/spree/billing/shared/_order_tabs.html.erb +0 -41
- data/app/views/spree/billing/shared/_pop_up_confirmation.html.erb +0 -103
- data/app/views/spree/billing/shared/_product_tabs.html.erb +0 -24
- data/app/views/spree/billing/shared/_role_tabs.html.erb +0 -13
- data/app/views/spree/billing/shared/_subscription_tabs.html.erb +0 -20
- data/app/views/spree/billing/shared/_user_tabs.html.erb +0 -13
- data/app/views/spree/billing/shared/_vendor_logo.html.erb +0 -1
- data/app/views/spree/billing/shared/_vendor_switcher.html.erb +0 -57
- data/app/views/spree/billing/shared/sortable_tree/_taxonomy.html.erb +0 -29
- data/app/views/spree/billing/shared/sub_menu/_product.html.erb +0 -8
- data/app/views/spree/billing/shared/sub_menu/_report.html.erb +0 -23
- data/app/views/spree/billing/shared/sub_menu/_setting.html.erb +0 -11
- data/app/views/spree/billing/store_credits/_form.html.erb +0 -11
- data/app/views/spree/billing/store_credits/index.html.erb +0 -44
- data/app/views/spree/billing/store_credits/new.html.erb +0 -17
- data/app/views/spree/billing/subscriptions/_form.html.erb +0 -27
- data/app/views/spree/billing/subscriptions/_subscribed_variants_table.html.erb +0 -24
- data/app/views/spree/billing/subscriptions/edit.html.erb +0 -12
- data/app/views/spree/billing/subscriptions/index.html.erb +0 -86
- data/app/views/spree/billing/subscriptions/new.html.erb +0 -21
- data/app/views/spree/billing/users/_form.html.erb +0 -64
- data/app/views/spree/billing/users/edit.html.erb +0 -25
- data/app/views/spree/billing/users/index.html.erb +0 -116
- data/app/views/spree/billing/users/new.html.erb +0 -17
- data/app/views/spree/billing/variants/_form.html.erb +0 -28
- data/app/views/spree/billing/variants/edit.html.erb +0 -15
- data/app/views/spree/billing/variants/index.html.erb +0 -66
- data/app/views/spree/billing/variants/new.html.erb +0 -15
- data/app/views/spree/billing/vendors/_form.html.erb +0 -75
- data/app/views/spree/billing/vendors/edit.html.erb +0 -9
- data/app/views/spree/layouts/billing.html.erb +0 -74
- data/app/views/spree/layouts/transit.html.erb +0 -74
- data/app/views/spree/transit/amenities/_form.html.erb +0 -50
- data/app/views/spree/transit/amenities/_option_value_fields.html.erb +0 -15
- data/app/views/spree/transit/amenities/edit.html.erb +0 -53
- data/app/views/spree/transit/branches/_filter_form.erb +0 -23
- data/app/views/spree/transit/branches/_form.html.erb +0 -36
- data/app/views/spree/transit/branches/edit.html.erb +0 -15
- data/app/views/spree/transit/branches/index.html.erb +0 -43
- data/app/views/spree/transit/branches/new.html.erb +0 -19
- data/app/views/spree/transit/locations/_filter_form.erb +0 -30
- data/app/views/spree/transit/locations/_form.html.erb +0 -19
- data/app/views/spree/transit/locations/edit.html.erb +0 -15
- data/app/views/spree/transit/locations/index.html.erb +0 -49
- data/app/views/spree/transit/locations/new.html.erb +0 -17
- data/app/views/spree/transit/places/_assets_form.html.erb +0 -8
- data/app/views/spree/transit/places/_form.html.erb +0 -128
- data/app/views/spree/transit/places/edit.html.erb +0 -21
- data/app/views/spree/transit/places/index.html.erb +0 -32
- data/app/views/spree/transit/places/new.html.erb +0 -16
- data/app/views/spree/transit/reservations/index.html.erb +0 -5
- data/app/views/spree/transit/routes/_filter_form.erb +0 -24
- data/app/views/spree/transit/routes/_form.html.erb +0 -74
- data/app/views/spree/transit/routes/edit.html.erb +0 -13
- data/app/views/spree/transit/routes/index.html.erb +0 -79
- data/app/views/spree/transit/routes/new.html.erb +0 -18
- data/app/views/spree/transit/service_calendars/_exception_rules.html.erb +0 -44
- data/app/views/spree/transit/service_calendars/_filters.html.erb +0 -17
- data/app/views/spree/transit/service_calendars/_form.html.erb +0 -107
- data/app/views/spree/transit/service_calendars/index.html.erb +0 -77
- data/app/views/spree/transit/service_calendars/new.html.erb +0 -13
- data/app/views/spree/transit/shared/_amenity_tabs.html.erb +0 -21
- data/app/views/spree/transit/shared/_header.html.erb +0 -20
- data/app/views/spree/transit/shared/_main_menu.html.erb +0 -34
- data/app/views/spree/transit/shared/_route_tabs.html.erb +0 -25
- data/app/views/spree/transit/shared/_vehicle_tabs.html.erb +0 -15
- data/app/views/spree/transit/shared/_vendor_switcher.html.erb +0 -40
- data/app/views/spree/transit/shared/sortable_tree/_taxonomy.html.erb +0 -29
- data/app/views/spree/transit/shared/sub_menu/_setting.html.erb +0 -3
- data/app/views/spree/transit/stops/_filters.html.erb +0 -33
- data/app/views/spree/transit/stops/_form.html.erb +0 -68
- data/app/views/spree/transit/stops/edit.html.erb +0 -16
- data/app/views/spree/transit/stops/index.html.erb +0 -58
- data/app/views/spree/transit/stops/new.html.erb +0 -16
- data/app/views/spree/transit/trip_stops/index.html.erb +0 -38
- data/app/views/spree/transit/trips/_form.html.erb +0 -82
- data/app/views/spree/transit/trips/edit.html.erb +0 -15
- data/app/views/spree/transit/trips/new.html.erb +0 -15
- data/app/views/spree/transit/vectors/amenity_values/index.html.erb +0 -111
- data/app/views/spree/transit/vehicle_photos/_form.html.erb +0 -22
- data/app/views/spree/transit/vehicle_photos/edit.html.erb +0 -9
- data/app/views/spree/transit/vehicle_photos/index.html.erb +0 -61
- data/app/views/spree/transit/vehicle_photos/new.html.erb +0 -9
- data/app/views/spree/transit/vehicle_seats/_form.html.erb +0 -48
- data/app/views/spree/transit/vehicle_seats/_seats.html.erb +0 -63
- data/app/views/spree/transit/vehicle_types/_filter_form.html.erb +0 -29
- data/app/views/spree/transit/vehicle_types/_form.html.erb +0 -65
- data/app/views/spree/transit/vehicle_types/_seat_view.erb +0 -83
- data/app/views/spree/transit/vehicle_types/edit.html.erb +0 -93
- data/app/views/spree/transit/vehicle_types/index.html.erb +0 -94
- data/app/views/spree/transit/vehicle_types/new.html.erb +0 -14
- data/app/views/spree/transit/vehicles/_filter_form.html.erb +0 -23
- data/app/views/spree/transit/vehicles/_form.html.erb +0 -19
- data/app/views/spree/transit/vehicles/edit.html.erb +0 -19
- data/app/views/spree/transit/vehicles/index.html.erb +0 -57
- data/app/views/spree/transit/vehicles/new.html.erb +0 -19
- data/app/views/spree/transit/vendors/_form.html.erb +0 -34
- data/app/views/spree/transit/vendors/_tabs.html.erb +0 -16
- data/app/views/spree/transit/vendors/edit.html.erb +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9473bef71175dc42b69c1959ac56827e1ca0ef2b6c13205ed2c83793e678a21f
|
|
4
|
+
data.tar.gz: 314a9fbe0dbc6fe925f435b3eaeef9d737689f81fb7d95e631bbcf10a2e3e8bf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa509b4c09684a80915bca9927228aa41de193821593d00f84da4cd1bb2f2b0d5034146d9c52e4dec654ad57092ac87273771be63841b66546cb1a1b63e0077d
|
|
7
|
+
data.tar.gz: f04d83d04ce10ebb051931a0bde85c690e279ac08d18e90b3be9e2f14721992c9e04096ef1eab051e1dde8b81ba6ea6aef356ca77422ac27ecb05aebf7f2e5c8
|
data/.env.example
CHANGED
|
@@ -86,3 +86,8 @@ PAYMENT_INCOMPLETE_NOTIFICATION_DELAY_IN_MINUTES=5 # Delay before notifying a c
|
|
|
86
86
|
|
|
87
87
|
# See: app/jobs/spree_cm_commissioner/telegram_alerts/order_integrity_check_job.rb
|
|
88
88
|
ORDER_INTEGRITY_TELEGRAM_ALERT_ENABLED="no" # Set to yes to enable the order integrity Telegram alert job (off by default to avoid flooding Telegram rate limits during traffic spikes)
|
|
89
|
+
|
|
90
|
+
# See: app/models/concerns/spree_cm_commissioner/product_type.rb
|
|
91
|
+
PRE_INVENTORY_DAYS_TRANSIT=180 # How many days ahead inventory items are pre-generated for transit variants
|
|
92
|
+
PRE_INVENTORY_DAYS_ACCOMMODATION=365 # How many days ahead inventory items are pre-generated for accommodation variants
|
|
93
|
+
PRE_INVENTORY_DAYS_SERVICE=180 # How many days ahead inventory items are pre-generated for service variants
|
data/Gemfile.lock
CHANGED
|
@@ -2,14 +2,6 @@
|
|
|
2
2
|
//= require map/map-picker
|
|
3
3
|
//= require spree_cm_commissioner/routes
|
|
4
4
|
//= require spree_cm_commissioner/calendar
|
|
5
|
-
//= require spree_cm_commissioner/role_form
|
|
6
|
-
//= require spree_cm_commissioner/address_hide_form
|
|
7
5
|
//= require spree_cm_commissioner/annual_calendar_form
|
|
8
|
-
//= require spree_cm_commissioner/report_hide_filter
|
|
9
|
-
//= require spree_cm_commissioner/update_popup_confirmation
|
|
10
|
-
//= require spree_cm_commissioner/vehicle_seat
|
|
11
|
-
//= require spree_cm_commissioner/selected_field_filter_origin_destination
|
|
12
|
-
//= require spree_cm_commissioner/location_checkbox_manager
|
|
13
|
-
//= require spree_cm_commissioner/trip_stop_selection
|
|
14
6
|
//= require spree_cm_commissioner/tenant_payment_icon_fields
|
|
15
7
|
//= require spree_cm_commissioner/copy_to_clipboard
|
|
@@ -2,5 +2,4 @@
|
|
|
2
2
|
@import "spree_cm_commissioner/backend/calendar";
|
|
3
3
|
@import "spree_cm_commissioner/backend/service_calendar";
|
|
4
4
|
@import "spree_cm_commissioner/backend/guest_qr_code";
|
|
5
|
-
@import "spree_cm_commissioner/backend/vehicle_seat";
|
|
6
5
|
@import "spree_cm_commissioner/backend/tenant_payment_icon_fields";
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Admin
|
|
3
3
|
module BaseControllerDecorator
|
|
4
|
-
def self.prepended(base)
|
|
5
|
-
base.before_action :redirect_to_billing, if: -> { Spree::Store.default.code.include?('billing') }
|
|
6
|
-
end
|
|
7
|
-
|
|
8
4
|
# even db in read only mode, authorizor still need to run Warden callbacks which will write to db
|
|
9
5
|
# this include update tracked fields such as sign_in_count, last_sign_in_at, etc.
|
|
10
6
|
def authorize!(*args)
|
|
@@ -44,14 +40,6 @@ module Spree
|
|
|
44
40
|
|
|
45
41
|
redirect_back fallback_location: admin_root_path
|
|
46
42
|
end
|
|
47
|
-
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
def redirect_to_billing
|
|
51
|
-
return if request.path.include?('/billing')
|
|
52
|
-
|
|
53
|
-
redirect_to billing_root_url unless spree_current_user.super_admin?
|
|
54
|
-
end
|
|
55
43
|
end
|
|
56
44
|
end
|
|
57
45
|
end
|
|
@@ -59,6 +59,17 @@ module Spree
|
|
|
59
59
|
redirect_back fallback_location: admin_product_stock_managements_path(@product)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# PATCH /products/:slug/inventory_items/bulk_update_closed
|
|
63
|
+
#
|
|
64
|
+
# Ids travel in the form body, not the query string: a range across a year can be hundreds of ids.
|
|
65
|
+
def bulk_update_closed
|
|
66
|
+
closed = ActiveModel::Type::Boolean.new.cast(params[:closed])
|
|
67
|
+
count = @product.inventory_items.where(id: params[:inventory_item_ids]).update_all(closed: closed, updated_at: Time.current) # rubocop:disable Rails/SkipsModelValidations
|
|
68
|
+
|
|
69
|
+
flash[:success] = "#{closed ? 'Closed' : 'Reopened'} #{count} inventory items for sale"
|
|
70
|
+
redirect_back fallback_location: admin_product_stock_managements_path(@product)
|
|
71
|
+
end
|
|
72
|
+
|
|
62
73
|
# PATCH /products/:slug/inventory_items/bulk_reset?inventory_item_ids=1,2,3
|
|
63
74
|
def bulk_reset
|
|
64
75
|
inventory_item_ids = params[:inventory_item_ids]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
# Same CRUD as ServiceFeeRulesController (scope picker, tenant dropdown, calculator partial),
|
|
4
|
+
# plus the payment method a rule applies to.
|
|
5
|
+
class PaymentFeeRulesController < Spree::Admin::ResourceController
|
|
6
|
+
helper SpreeCmCommissioner::Admin::FeeRulesHelper
|
|
7
|
+
helper_method :selected_tenant
|
|
8
|
+
|
|
9
|
+
before_action :load_data
|
|
10
|
+
|
|
11
|
+
def index
|
|
12
|
+
@search = model_class.ransack(params[:q])
|
|
13
|
+
@status = %w[active inactive].include?(params[:status]) ? params[:status] : 'all'
|
|
14
|
+
|
|
15
|
+
scope = @search.result.includes(:payment_method, :ruleable, :calculator, :tenant)
|
|
16
|
+
scope = scope.where(active: true) if @status == 'active'
|
|
17
|
+
scope = scope.where(active: false) if @status == 'inactive'
|
|
18
|
+
# Same tenant semantics as ServiceFeeRulesController#index.
|
|
19
|
+
scope = scope.where(tenant_id: selected_tenant&.id)
|
|
20
|
+
|
|
21
|
+
@payment_fee_rules = scope.order(ruleable_type: :asc, ruleable_id: :asc, payment_method_id: :asc, currency: :asc)
|
|
22
|
+
.page(params[:page])
|
|
23
|
+
.per(25)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# See ServiceFeeRulesController#build_resource: tenant_id comes from the URL only.
|
|
27
|
+
def build_resource
|
|
28
|
+
model_class.new(tenant_id: params[:tenant_id].presence)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def load_data
|
|
34
|
+
@calculators = model_class.available_calculator_types.sort_by(&:name)
|
|
35
|
+
@tenants ||= SpreeCmCommissioner::Tenant.order(:name)
|
|
36
|
+
@payment_methods = Spree::PaymentMethod.includes(:vendor).order(:name)
|
|
37
|
+
@currencies = (current_store.supported_currencies_list.map(&:iso_code) | [@object&.currency]).compact.sort
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def selected_tenant
|
|
41
|
+
return @selected_tenant if defined?(@selected_tenant)
|
|
42
|
+
|
|
43
|
+
@selected_tenant = params[:tenant_id].presence && SpreeCmCommissioner::Tenant.find_by(id: params[:tenant_id])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def model_class
|
|
47
|
+
SpreeCmCommissioner::PaymentFeeRule
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def object_name
|
|
51
|
+
'spree_cm_commissioner_payment_fee_rule'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def collection_url
|
|
55
|
+
admin_payment_fee_rules_url
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def location_after_save
|
|
59
|
+
edit_admin_payment_fee_rule_path(@object)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# tenant_id is deliberately excluded -- see build_resource.
|
|
63
|
+
def permitted_resource_params
|
|
64
|
+
params.require(:spree_cm_commissioner_payment_fee_rule).permit(
|
|
65
|
+
:payment_method_id,
|
|
66
|
+
:ruleable_type,
|
|
67
|
+
:ruleable_id,
|
|
68
|
+
:active,
|
|
69
|
+
:currency,
|
|
70
|
+
:calculator_type,
|
|
71
|
+
calculator_attributes: %i[id preferred_percent preferred_flat_amount]
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Storefront
|
|
5
|
+
# GET /api/v2/storefront/cart_payment_fees?payment_method_ids[]=1&payment_method_ids[]=2
|
|
6
|
+
#
|
|
7
|
+
# Payment fee each requested payment method would add to the current cart. Order-scoped
|
|
8
|
+
# and uncached on purpose: the answer depends on line items and applied store credit, both
|
|
9
|
+
# of which change on the payment screen. The client skips methods whose catalog entry says
|
|
10
|
+
# `fee_configured: false`, so carts with no fees configured never hit this.
|
|
11
|
+
class CartPaymentFeesController < Spree::Api::V2::ResourceController
|
|
12
|
+
include OrderConcern
|
|
13
|
+
|
|
14
|
+
before_action :ensure_order
|
|
15
|
+
|
|
16
|
+
# override
|
|
17
|
+
def index
|
|
18
|
+
render_serialized_payload do
|
|
19
|
+
collection_serializer.new(collection).serializable_hash
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# override
|
|
24
|
+
def collection
|
|
25
|
+
@collection ||= requested_payment_methods.map do |payment_method|
|
|
26
|
+
SpreeCmCommissioner::PaymentFees::Preview.call(order: spree_current_order, payment_method: payment_method)
|
|
27
|
+
.value[:payment_fee_preview]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# override
|
|
32
|
+
def collection_serializer
|
|
33
|
+
SpreeCmCommissioner::V2::Storefront::PaymentFeePreviewSerializer
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# Ids the order can't actually pay with are dropped rather than rejected, so a stale client
|
|
39
|
+
# list (e.g. a method disabled since the catalog was cached) still gets the rest.
|
|
40
|
+
def requested_payment_methods
|
|
41
|
+
ids = Array(params[:payment_method_ids]).map(&:to_s)
|
|
42
|
+
spree_current_order.available_payment_methods.select { |pm| ids.include?(pm.id.to_s) }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -10,6 +10,8 @@ module Spree
|
|
|
10
10
|
class SeatLayoutsController < Spree::Api::V2::ResourceController
|
|
11
11
|
# GET /api/v2/storefront/seat_layouts/:id
|
|
12
12
|
# - include_details=true|false (optional, default: true)
|
|
13
|
+
#
|
|
14
|
+
# Always returns `product_ids` (see SeatLayout#product_ids).
|
|
13
15
|
def show
|
|
14
16
|
render_serialized_payload { serialize_resource(resource) }
|
|
15
17
|
end
|
|
@@ -70,7 +72,7 @@ module Spree
|
|
|
70
72
|
|
|
71
73
|
# override
|
|
72
74
|
def serializer_params
|
|
73
|
-
super.merge(include_details: include_details
|
|
75
|
+
super.merge(include_details: include_details?, include_product_ids: action_name == 'show')
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
# default return true
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
# See Spree::Api::V2::Storefront::CartPaymentFeesController.
|
|
6
|
+
class CartPaymentFeesController < BaseController
|
|
7
|
+
include Spree::Api::V2::Storefront::OrderConcern
|
|
8
|
+
|
|
9
|
+
before_action :ensure_order
|
|
10
|
+
|
|
11
|
+
# override
|
|
12
|
+
def index
|
|
13
|
+
render_serialized_payload do
|
|
14
|
+
collection_serializer.new(collection).serializable_hash
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# override
|
|
19
|
+
def collection
|
|
20
|
+
@collection ||= requested_payment_methods.map do |payment_method|
|
|
21
|
+
SpreeCmCommissioner::PaymentFees::Preview.call(order: spree_current_order, payment_method: payment_method)
|
|
22
|
+
.value[:payment_fee_preview]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# override
|
|
27
|
+
def collection_serializer
|
|
28
|
+
SpreeCmCommissioner::V2::Storefront::PaymentFeePreviewSerializer
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Ids the order can't actually pay with are dropped rather than rejected, so a stale client
|
|
34
|
+
# list (e.g. a method disabled since the catalog was cached) still gets the rest.
|
|
35
|
+
def requested_payment_methods
|
|
36
|
+
ids = Array(params[:payment_method_ids]).map(&:to_s)
|
|
37
|
+
spree_current_order.available_payment_methods.select { |pm| ids.include?(pm.id.to_s) }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
module Spree
|
|
2
|
-
module
|
|
2
|
+
module Admin
|
|
3
3
|
module NavigationHelperDecorator
|
|
4
|
-
def billing_main_menu_tree(text, icon: nil, sub_menu: nil, url: '#')
|
|
5
|
-
content_tag :li, class: 'sidebar-menu-item d-block w-100 text-muted' do
|
|
6
|
-
main_menu_item(text, url: url, icon: icon) +
|
|
7
|
-
render(partial: "spree/billing/shared/sub_menu/#{sub_menu}")
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
|
|
11
4
|
def customer_per_page_dropdown
|
|
12
5
|
per_page_default = Spree::Backend::Config.admin_orders_per_page
|
|
13
6
|
per_page_options = %w[24 48 120 200 400 600]
|
|
@@ -23,4 +16,4 @@ module Spree
|
|
|
23
16
|
end
|
|
24
17
|
end
|
|
25
18
|
|
|
26
|
-
Spree::Admin::NavigationHelper.prepend(Spree::
|
|
19
|
+
Spree::Admin::NavigationHelper.prepend(Spree::Admin::NavigationHelperDecorator)
|
|
@@ -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
|
|
@@ -18,6 +18,8 @@ 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!
|
|
21
23
|
|
|
22
24
|
# Fails fast, before any of the other before_transition/around_transition work below runs
|
|
23
25
|
# (external-system unstock, Redis unstock, notifications, ...), when the inventory hold has
|
|
@@ -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
|
|
@@ -7,7 +7,11 @@ module SpreeCmCommissioner
|
|
|
7
7
|
|
|
8
8
|
PRODUCT_TYPES = %i[accommodation service ecommerce transit show_episode vote_package advertisement].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')
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
# Shared scoping for fee rules that resolve product -> event -> vendor -> tenant/global
|
|
3
|
+
# (ServiceFeeRule, PaymentFeeRule). Each including model keeps its own uniqueness
|
|
4
|
+
# validation, since what makes a rule unique differs (PaymentFeeRule adds payment_method_id).
|
|
5
|
+
module ScopedFeeRule
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
ALLOWED_RULEABLE_TYPES = %w[Spree::Product Spree::Taxon Spree::Vendor].freeze
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
belongs_to :ruleable, polymorphic: true, optional: true
|
|
12
|
+
|
|
13
|
+
# Only meaningful on a ruleable-less row (see the tenant_id absence validation below): a
|
|
14
|
+
# tenant's own default, distinct from the true platform-wide default (tenant_id AND ruleable
|
|
15
|
+
# both nil). Not used on Product/Vendor/Event-scoped rows — their tenant is already implied by
|
|
16
|
+
# the ruleable itself (a vendor belongs to exactly one tenant), so storing it there too would
|
|
17
|
+
# just be a second copy that could drift out of sync.
|
|
18
|
+
multi_tenant :tenant, class_name: 'SpreeCmCommissioner::Tenant'
|
|
19
|
+
|
|
20
|
+
before_validation :normalize_ruleable, :normalize_currency
|
|
21
|
+
|
|
22
|
+
validates :currency, presence: true
|
|
23
|
+
validates :ruleable_type, inclusion: { in: ALLOWED_RULEABLE_TYPES }, allow_nil: true
|
|
24
|
+
validates :ruleable_id, presence: true, if: -> { ruleable_type.present? }
|
|
25
|
+
validates :tenant_id, absence: true, if: -> { ruleable_type.present? }
|
|
26
|
+
|
|
27
|
+
scope :active_rules, -> { where(active: true) }
|
|
28
|
+
scope :for_currency, -> (currency) { where(currency: currency.to_s.upcase) }
|
|
29
|
+
|
|
30
|
+
# Finds the active rule for the given ruleable (nil = global) and currency.
|
|
31
|
+
scope :active_for, lambda { |ruleable:, currency:|
|
|
32
|
+
active_rules
|
|
33
|
+
.for_currency(currency)
|
|
34
|
+
.where(ruleable_type: ruleable&.class&.base_class&.name, ruleable_id: ruleable&.id)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Finds the ruleable-less rule for a specific tenant (nil tenant_id = true platform-wide).
|
|
38
|
+
scope :active_for_tenant, lambda { |tenant_id:, currency:|
|
|
39
|
+
active_rules
|
|
40
|
+
.for_currency(currency)
|
|
41
|
+
.where(ruleable_type: nil, ruleable_id: nil, tenant_id: tenant_id)
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# "Name (slug)" for a product/event/vendor, so same-named records can be told apart in the
|
|
46
|
+
# admin scope-target picker. Events (taxons) show their permalink first: that's what the
|
|
47
|
+
# picker searches them by, and it reads clearer than the taxon's generated slug.
|
|
48
|
+
def self.ruleable_label(record)
|
|
49
|
+
name = record.respond_to?(:pretty_name) ? record.pretty_name : record.try(:name)
|
|
50
|
+
slug = record.try(:permalink).presence || record.try(:slug).presence
|
|
51
|
+
slug ? "#{name} (#{slug})" : name.to_s
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class_methods do
|
|
55
|
+
# Walks product -> event -> vendor -> global within `relation`, returning the first active
|
|
56
|
+
# rule that matches the currency. The product/event/vendor leg mirrors
|
|
57
|
+
# SpreeCmCommissioner::TicketTransfers::FeeCalculator's precedence.
|
|
58
|
+
#
|
|
59
|
+
# "Global" means two different things depending on whether the vendor belongs to a tenant:
|
|
60
|
+
# - No tenant (a base BookMe+ vendor): the true platform-wide default (tenant_id nil).
|
|
61
|
+
# - Has a tenant: ONLY that tenant's own default (tenant_id: vendor.tenant_id) — deliberately
|
|
62
|
+
# does NOT fall back to the platform-wide default if the tenant hasn't set one. Each
|
|
63
|
+
# white-label tenant is expected to configure their own fee (or explicitly have none); a
|
|
64
|
+
# tenant silently inheriting BookMe+'s own fee because they forgot to configure one would be
|
|
65
|
+
# a surprising, not a sensible, default.
|
|
66
|
+
#
|
|
67
|
+
# Runs outside MultiTenant scoping: tenant API requests set a current tenant, which would
|
|
68
|
+
# filter every query to `tenant_id = <tenant>` and so skip product/event/vendor rules (they
|
|
69
|
+
# never store a tenant_id). Tenancy is handled explicitly above instead.
|
|
70
|
+
def resolve_scoped(line_item:, currency:, relation: all)
|
|
71
|
+
MultiTenant.without do
|
|
72
|
+
ruleables_for(line_item).each do |ruleable|
|
|
73
|
+
rule = relation.active_for(ruleable: ruleable, currency: currency).first
|
|
74
|
+
return rule if rule.present?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
relation.active_for_tenant(tenant_id: vendor_for(line_item)&.tenant_id, currency: currency).first
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Compacted because active_for(ruleable: nil) means "ruleable-less", i.e. the global/tenant
|
|
82
|
+
# rows: a line item without an event would otherwise match a global rule before ever
|
|
83
|
+
# checking its vendor.
|
|
84
|
+
def ruleables_for(line_item)
|
|
85
|
+
[line_item.product, line_item.event, vendor_for(line_item)].compact
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def vendor_for(line_item)
|
|
89
|
+
product = line_item.product
|
|
90
|
+
product.respond_to?(:vendor) ? product.vendor : nil
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def scope_label
|
|
95
|
+
if ruleable.blank?
|
|
96
|
+
# A tenant-scoped row is that tenant's own "global" — it never falls back to the
|
|
97
|
+
# BookMe+-level one (see resolve_scoped), so it isn't really "global" in the broad sense;
|
|
98
|
+
# labeled as its own Tenant scope rather than reusing the word "Global" for both.
|
|
99
|
+
return Spree.t(:service_fee_rule_scope_tenant, name: tenant&.name || "##{tenant_id}") if tenant_id.present?
|
|
100
|
+
|
|
101
|
+
return Spree.t(:service_fee_rule_scope_global)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
label_value = ruleable.respond_to?(:name) ? ruleable.name.to_s : "##{ruleable_id}"
|
|
105
|
+
"#{ruleable_type_label}: #{label_value}"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def ruleable_type_label
|
|
109
|
+
case ruleable_type
|
|
110
|
+
when 'Spree::Product' then Spree.t(:service_fee_rule_scope_product)
|
|
111
|
+
when 'Spree::Taxon' then Spree.t(:service_fee_rule_scope_event)
|
|
112
|
+
when 'Spree::Vendor' then Spree.t(:service_fee_rule_scope_vendor)
|
|
113
|
+
else ruleable_type&.demodulize || Spree.t(:service_fee_rule_scope_global)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
# "Global" dropdown sends "" not nil. Convert to nil so validations and uniqueness checks work.
|
|
120
|
+
def normalize_ruleable
|
|
121
|
+
self.ruleable_type = nil if ruleable_type.blank?
|
|
122
|
+
self.ruleable_id = nil if ruleable_type.nil?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def normalize_currency
|
|
126
|
+
self.currency = currency.to_s.upcase.presence
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -4,6 +4,7 @@ module SpreeCmCommissioner
|
|
|
4
4
|
base.belongs_to :payable, polymorphic: true, optional: true
|
|
5
5
|
base.scope :pricing_action, -> { where(source_type: 'SpreeCmCommissioner::PricingAction') }
|
|
6
6
|
base.scope :service_fee, -> { where(source_type: 'SpreeCmCommissioner::ServiceFeeRule') }
|
|
7
|
+
base.scope :payment_fee, -> { where(source_type: 'SpreeCmCommissioner::PaymentFeeRule') }
|
|
7
8
|
base.whitelisted_ransackable_attributes |= %w[payable_id]
|
|
8
9
|
end
|
|
9
10
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Calculators
|
|
3
|
+
class PaymentFeeFlatAmount < Spree::Calculator
|
|
4
|
+
preference :flat_amount, :decimal, default: 0
|
|
5
|
+
|
|
6
|
+
def self.description
|
|
7
|
+
Spree.t(:payment_fee_flat_amount)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Once per payment, not per unit: providers bill per transaction. Nothing to charge when
|
|
11
|
+
# store credit covers the whole order and no gateway payment happens.
|
|
12
|
+
def compute(base)
|
|
13
|
+
base.to_d.positive? ? preferred_flat_amount : 0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def fee_description(currency:)
|
|
17
|
+
Spree::Money.new(preferred_flat_amount, currency: currency).to_s
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Calculators
|
|
3
|
+
class PaymentFeePercent < Spree::Calculator
|
|
4
|
+
include SpreeCmCommissioner::PercentFeeCalculable
|
|
5
|
+
|
|
6
|
+
def self.description
|
|
7
|
+
Spree.t(:payment_fee_percent)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# `base` is the share of the gateway amount this rule covers (see PaymentFees::Preview),
|
|
11
|
+
# not a line item: providers bill per transaction, so the flat part applies once per payment.
|
|
12
|
+
def compute(base)
|
|
13
|
+
percent_plus_flat(base.to_d, preferred_flat_amount)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module Calculators
|
|
3
3
|
class ServiceFeePercent < Spree::Calculator
|
|
4
|
-
|
|
4
|
+
include SpreeCmCommissioner::PercentFeeCalculable
|
|
5
5
|
|
|
6
6
|
def self.description
|
|
7
7
|
Spree.t(:service_fee_percent)
|
|
@@ -12,8 +12,11 @@ module SpreeCmCommissioner
|
|
|
12
12
|
# Spree::TaxRate.store_pre_tax_amount running for this line item, so it would make
|
|
13
13
|
# service-fee computation order-dependent on tax having already run. discounted_amount
|
|
14
14
|
# is a live computed value (amount + taxable_adjustment_total) with no such dependency.
|
|
15
|
+
#
|
|
16
|
+
# The flat part is per unit, same as ServiceFeeFlatAmount, so "10% + $0.25" adds $0.25 per
|
|
17
|
+
# ticket regardless of how tickets are grouped into lines.
|
|
15
18
|
def compute(line_item)
|
|
16
|
-
line_item.discounted_amount *
|
|
19
|
+
percent_plus_flat(line_item.discounted_amount, preferred_flat_amount * line_item.quantity)
|
|
17
20
|
end
|
|
18
21
|
end
|
|
19
22
|
end
|
|
@@ -109,6 +109,10 @@ module SpreeCmCommissioner
|
|
|
109
109
|
# This method is only used when admin update stock
|
|
110
110
|
def adjust_quantity!(quantity)
|
|
111
111
|
with_lock do
|
|
112
|
+
# Seed a cold Redis key from the DB value before applying the delta: adjust_quantity_in_redis treats a
|
|
113
|
+
# missing key as 0, so it would otherwise cache only the delta (e.g. 1 for a row the DB holds at 3).
|
|
114
|
+
SpreeCmCommissioner::RedisStock::CachedInventoryItemsBuilder.new([self]).call
|
|
115
|
+
|
|
112
116
|
# IMPORTANT: Apply quantity changes directly without defensive clamping.
|
|
113
117
|
# The model validation will catch any attempts to go negative, surfacing bugs
|
|
114
118
|
# in upstream logic rather than silently losing data.
|
|
@@ -170,12 +174,13 @@ module SpreeCmCommissioner
|
|
|
170
174
|
inventory_date.nil? || inventory_date >= Time.zone.today
|
|
171
175
|
end
|
|
172
176
|
|
|
173
|
-
#
|
|
174
|
-
#
|
|
175
|
-
# Why even for permanent stock? Because if we expire it too soon (e.g. right after inventory_date),
|
|
176
|
-
# admin usually still need to look up past/old inventory items or adjust stock when needed.
|
|
177
|
+
# Dated keys expire a week after their date, or a day from now for an old date touched later. Expiring
|
|
178
|
+
# is safe: CachedInventoryItemsBuilder re-warms any cold key from the DB, past dates included.
|
|
177
179
|
def redis_expired_in
|
|
178
|
-
|
|
180
|
+
return 1.year.to_i if inventory_date.nil?
|
|
181
|
+
|
|
182
|
+
expires_at = [inventory_date.end_of_day + 7.days, 1.day.from_now].max
|
|
183
|
+
(expires_at - Time.zone.now).to_i
|
|
179
184
|
end
|
|
180
185
|
end
|
|
181
186
|
end
|