diff --git a/app/assets/javascripts/admin/subscriptions/controllers/subscription_controller.js.coffee b/app/assets/javascripts/admin/subscriptions/controllers/subscription_controller.js.coffee index 3b26866125..aa635da8cc 100644 --- a/app/assets/javascripts/admin/subscriptions/controllers/subscription_controller.js.coffee +++ b/app/assets/javascripts/admin/subscriptions/controllers/subscription_controller.js.coffee @@ -6,7 +6,11 @@ angular.module("admin.subscriptions").controller "SubscriptionController", ($sco $scope.schedules = Schedules.all $scope.paymentMethods = PaymentMethods.all $scope.shippingMethods = ShippingMethods.all - $scope.distributor_id = $scope.subscription.shop_id # variant selector requires distributor_id + + # Variant selector requires these + $scope.distributor_id = $scope.subscription.shop_id + $scope.eligible_for_subscriptions = true + $scope.view = if $scope.subscription.id? then 'review' else 'details' $scope.nextCallbacks = {} $scope.backCallbacks = {} diff --git a/app/assets/javascripts/admin/utils/directives/variant_autocomplete.js.coffee b/app/assets/javascripts/admin/utils/directives/variant_autocomplete.js.coffee index 9b8f12d3dc..17a467e463 100644 --- a/app/assets/javascripts/admin/utils/directives/variant_autocomplete.js.coffee +++ b/app/assets/javascripts/admin/utils/directives/variant_autocomplete.js.coffee @@ -22,6 +22,7 @@ angular.module("admin.utils").directive "variantAutocomplete", ($timeout) -> q: term distributor_id: scope.distributor_id order_cycle_id: scope.order_cycle_id + eligible_for_subscriptions: scope.eligible_for_subscriptions results: (data, page) -> results: data formatResult: (variant) -> diff --git a/app/controllers/admin/subscription_line_items_controller.rb b/app/controllers/admin/subscription_line_items_controller.rb index d981b71fe1..8c0432578d 100644 --- a/app/controllers/admin/subscription_line_items_controller.rb +++ b/app/controllers/admin/subscription_line_items_controller.rb @@ -1,6 +1,7 @@ require 'open_food_network/permissions' require 'open_food_network/order_cycle_permissions' require 'open_food_network/scope_variant_to_hub' +require "open_food_network/subscription_service" module Admin class SubscriptionLineItemsController < ResourceController @@ -26,7 +27,7 @@ module Admin @shop = Enterprise.managed_by(spree_current_user).find_by_id(params[:shop_id]) @schedule = permissions.editable_schedules.find_by_id(params[:schedule_id]) @order_cycle = @schedule.andand.current_or_next_order_cycle - @variant = Spree::Variant.stockable_by(@shop).find_by_id(params[:subscription_line_item][:variant_id]) + @variant = variant_if_eligible(params[:subscription_line_item][:variant_id]) if @shop.present? end def new_actions @@ -50,5 +51,9 @@ module Admin OpenFoodNetwork::ScopeVariantToHub.new(@shop).scope(@variant) @variant.price + fee_calculator.indexed_fees_for(@variant) end + + def variant_if_eligible(variant_id) + OpenFoodNetwork::SubscriptionService.eligible_variants(@shop).find_by_id(variant_id) + end end end diff --git a/app/services/subscription_validator.rb b/app/services/subscription_validator.rb index 33fc2baf77..8ee43b9c4f 100644 --- a/app/services/subscription_validator.rb +++ b/app/services/subscription_validator.rb @@ -2,6 +2,8 @@ # Public interface consists of #valid? method provided by ActiveModel::Validations # and #json_errors which compiles a serializable hash of errors +require "open_food_network/subscription_service" + class SubscriptionValidator include ActiveModel::Naming include ActiveModel::Conversion @@ -97,15 +99,12 @@ class SubscriptionValidator errors.add(:subscription_line_items, :not_available, name: name) end - # TODO: Extract this into a separate class def available_variant_ids - @available_variant_ids ||= - Spree::Variant.joins(exchanges: { order_cycle: :schedules }) - .where(id: subscription_line_items.map(&:variant_id)) - .where(schedules: { id: schedule }, exchanges: { incoming: false, receiver_id: shop }) - .merge(OrderCycle.not_closed) - .select('DISTINCT spree_variants.id') - .pluck(:id) + return @available_variant_ids if @available_variant_ids.present? + + subscription_variant_ids = subscription_line_items.map(&:variant_id) + @available_variant_ids = OpenFoodNetwork::SubscriptionService.eligible_variants(shop) + .where(id: subscription_variant_ids).pluck(:id) end def build_msg_from(k, msg) diff --git a/lib/open_food_network/scope_variants_for_search.rb b/lib/open_food_network/scope_variants_for_search.rb index 9befef5304..c1f90df7d1 100644 --- a/lib/open_food_network/scope_variants_for_search.rb +++ b/lib/open_food_network/scope_variants_for_search.rb @@ -5,6 +5,8 @@ require 'open_food_network/scope_variant_to_hub' # Further restrictions on the schedule, order_cycle or distributor through which the # products are available are also possible +require "open_food_network/subscription_service" + module OpenFoodNetwork class ScopeVariantsForSearch def initialize(params) @@ -33,6 +35,10 @@ module OpenFoodNetwork Spree::Variant.where(is_master: false).ransack(search_params.merge(m: 'or')).result end + def distributor + Enterprise.find params[:distributor_id] + end + def scope_to_schedule @variants = @variants.in_schedule(params[:schedule_id]) end @@ -42,12 +48,29 @@ module OpenFoodNetwork end def scope_to_distributor - distributor = Enterprise.find params[:distributor_id] + if params[:eligible_for_subscriptions] + scope_to_eligible_for_subscriptions_in_distributor + else + scope_to_available_for_orders_in_distributor + end + end + + def scope_to_available_for_orders_in_distributor @variants = @variants.in_distributor(distributor) + scope_variants_to_distributor(@variants, distributor) + end + + def scope_to_eligible_for_subscriptions_in_distributor + eligible_variants_scope = OpenFoodNetwork::SubscriptionService.eligible_variants(distributor) + @variants = @variants.merge(eligible_variants_scope) + scope_variants_to_distributor(@variants, distributor) + end + + def scope_variants_to_distributor(variants, distributor) scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor) # Perform scoping after all filtering is done. # Filtering could be a problem on scoped variants. - @variants.each { |v| scoper.scope(v) } + variants.each { |v| scoper.scope(v) } end end end diff --git a/lib/open_food_network/subscription_service.rb b/lib/open_food_network/subscription_service.rb new file mode 100644 index 0000000000..e3479275c1 --- /dev/null +++ b/lib/open_food_network/subscription_service.rb @@ -0,0 +1,28 @@ +module OpenFoodNetwork + class SubscriptionService + # Includes the following variants: + # - Variants of permitted producers + # - Variants of hub + # - Variants that are in outgoing exchanges where the hub is receiver + def self.eligible_variants(distributor) + permitted_order_cycle_enterprise_ids = EnterpriseRelationship.permitting(distributor) + .with_permission(:add_to_order_cycle).pluck(:parent_id) + permitted_producer_ids = Enterprise.is_primary_producer + .where('enterprises.id IN (?)', permitted_order_cycle_enterprise_ids).pluck(:id) + + outgoing_exchange_variant_ids = ExchangeVariant + .select("DISTINCT exchange_variants.variant_id") + .joins(:exchange) + .where(exchanges: { incoming: false, receiver_id: distributor.id }) + .pluck(:variant_id) + + variant_conditions = ["spree_products.supplier_id IN (?)", permitted_producer_ids | [distributor.id]] + if outgoing_exchange_variant_ids.present? + variant_conditions[0] << " OR spree_variants.id IN (?)" + variant_conditions << outgoing_exchange_variant_ids + end + + Spree::Variant.joins(:product).where(is_master: false).where(*variant_conditions) + end + end +end diff --git a/spec/controllers/admin/subscription_line_items_controller_spec.rb b/spec/controllers/admin/subscription_line_items_controller_spec.rb index b2f3a0f432..2b42a20df7 100644 --- a/spec/controllers/admin/subscription_line_items_controller_spec.rb +++ b/spec/controllers/admin/subscription_line_items_controller_spec.rb @@ -10,9 +10,9 @@ describe Admin::SubscriptionLineItemsController, type: :controller do let(:unmanaged_shop) { create(:enterprise) } let!(:product) { create(:product) } let!(:variant) { create(:variant, product: product, unit_value: '100', price: 15.00, option_values: []) } + let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [variant], enterprise_fees: [enterprise_fee]) } let!(:enterprise_fee) { create(:enterprise_fee, amount: 3.50) } let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now, orders_close_at: 7.days.from_now) } - let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [variant], enterprise_fees: [enterprise_fee]) } let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } let(:unmanaged_schedule) { create(:schedule, order_cycles: [create(:simple_order_cycle, coordinator: unmanaged_shop)]) } @@ -42,6 +42,8 @@ describe Admin::SubscriptionLineItemsController, type: :controller do before { params.merge!(shop_id: shop.id) } context "but the shop doesn't have permission to sell product in question" do + let!(:outgoing_exchange) { } + it "returns an error" do spree_post :build, params json_response = JSON.parse(response.body) diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb index 5f83bf0529..9385d453f3 100644 --- a/spec/controllers/admin/subscriptions_controller_spec.rb +++ b/spec/controllers/admin/subscriptions_controller_spec.rb @@ -341,7 +341,7 @@ describe Admin::SubscriptionsController, type: :controller do end context 'with subscription_line_items params' do - let!(:product2) { create(:product, supplier: shop) } + let!(:product2) { create(:product) } let!(:variant2) { create(:variant, product: product2, unit_value: '1000', price: 6.00, option_values: []) } before do diff --git a/spec/features/admin/subscriptions_spec.rb b/spec/features/admin/subscriptions_spec.rb index be2eaa6ba5..0a03d629f4 100644 --- a/spec/features/admin/subscriptions_spec.rb +++ b/spec/features/admin/subscriptions_spec.rb @@ -145,13 +145,13 @@ feature 'Subscriptions' do let!(:customer_user) { create(:user) } let!(:credit_card1) { create(:credit_card, user: customer_user, cc_type: 'visa', last_digits: 1111, month: 10, year: 2030) } let!(:customer) { create(:customer, enterprise: shop, bill_address: address, user: customer_user, allow_charges: true) } - let!(:product1) { create(:product, supplier: shop) } - let!(:product2) { create(:product, supplier: shop) } - let!(:variant1) { create(:variant, product: product1, unit_value: '100', price: 12.00, option_values: []) } - let!(:variant2) { create(:variant, product: product2, unit_value: '1000', price: 6.00, option_values: []) } + let!(:test_product) { create(:product, supplier: shop, distributors: []) } + let!(:test_variant) { create(:variant, product: test_product, unit_value: "100", price: 12.00, option_values: []) } + let!(:shop_product) { create(:product, supplier: shop, distributors: [shop]) } + let!(:shop_variant) { create(:variant, product: shop_product, unit_value: "1000", price: 6.00, option_values: []) } let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now, orders_close_at: 7.days.from_now) } - let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [variant1, variant2], enterprise_fees: [enterprise_fee]) } + let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [test_variant, shop_variant], enterprise_fees: [enterprise_fee]) } let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } let!(:payment_method) { create(:stripe_payment_method, name: 'Credit Card', distributors: [shop], preferred_enterprise_id: shop.id) } let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } @@ -217,11 +217,9 @@ feature 'Subscriptions' do expect(page).to have_content 'Please add at least one product' # Adding a product and getting a price estimate - select2_search_async product1.name, from: I18n.t(:name_or_sku), dropdown_css: '.select2-drop' - fill_in 'add_quantity', with: 2 - click_link 'Add' + add_variant_to_subscription test_variant, 2 within 'table#subscription-line-items tr.item', match: :first do - expect(page).to have_selector 'td.description', text: "#{product1.name} - #{variant1.full_name}" + expect(page).to have_selector '.description', text: "#{test_product.name} - #{test_variant.full_name}" expect(page).to have_selector 'td.price', text: "$13.75" expect(page).to have_input 'quantity', with: "2" expect(page).to have_selector 'td.total', text: "$27.50" @@ -243,11 +241,9 @@ feature 'Subscriptions' do click_button('edit-products') # Adding a new product - select2_search_async product2.name, from: I18n.t(:name_or_sku), dropdown_css: '.select2-drop' - fill_in 'add_quantity', with: 3 - click_link 'Add' + add_variant_to_subscription shop_variant, 3 within 'table#subscription-line-items tr.item', match: :first do - expect(page).to have_selector 'td.description', text: "#{product2.name} - #{variant2.full_name}" + expect(page).to have_selector '.description', text: "#{shop_product.name} - #{shop_variant.full_name}" expect(page).to have_selector 'td.price', text: "$7.75" expect(page).to have_input 'quantity', with: "3" expect(page).to have_selector 'td.total', text: "$23.25" @@ -266,7 +262,7 @@ feature 'Subscriptions' do # Prices are shown in the index within 'table#subscription-line-items tr.item', match: :first do - expect(page).to have_selector 'td.description', text: "#{product2.name} - #{variant2.full_name}" + expect(page).to have_selector '.description', text: "#{shop_product.name} - #{shop_variant.full_name}" expect(page).to have_selector 'td.price', text: "$7.75" expect(page).to have_input 'quantity', with: "3" expect(page).to have_selector 'td.total', text: "$23.25" @@ -284,7 +280,7 @@ feature 'Subscriptions' do # Standing Line Items are created expect(subscription.subscription_line_items.count).to eq 1 subscription_line_item = subscription.subscription_line_items.first - expect(subscription_line_item.variant).to eq variant2 + expect(subscription_line_item.variant).to eq shop_variant expect(subscription_line_item.quantity).to eq 3 end end @@ -336,7 +332,7 @@ feature 'Subscriptions' do # Existing products should be visible click_button 'edit-products' within "#sli_0" do - expect(page).to have_selector 'td.description', text: "#{product1.name} - #{variant1.full_name}" + expect(page).to have_selector '.description', text: "#{product1.name} - #{variant1.full_name}" expect(page).to have_selector 'td.price', text: "$13.75" expect(page).to have_input 'quantity', with: "2" expect(page).to have_selector 'td.total', text: "$27.50" @@ -350,11 +346,9 @@ feature 'Subscriptions' do expect(page).to have_content 'Please add at least one product' # Add variant2 to the subscription - select2_search_async product2.name, from: I18n.t(:name_or_sku), dropdown_css: '.select2-drop' - fill_in 'add_quantity', with: 1 - click_link 'Add' + add_variant_to_subscription(variant2, 1) within "#sli_0" do - expect(page).to have_selector 'td.description', text: "#{product2.name} - #{variant2.full_name}" + expect(page).to have_selector '.description', text: "#{product2.name} - #{variant2.full_name}" expect(page).to have_selector 'td.price', text: "$7.75" expect(page).to have_input 'quantity', with: "1" expect(page).to have_selector 'td.total', text: "$7.75" @@ -364,11 +358,9 @@ feature 'Subscriptions' do expect(page).to have_selector '#order_form_total', text: "$7.75" # Add variant3 to the subscription (even though it is not available) - select2_search_async product3.name, from: I18n.t(:name_or_sku), dropdown_css: '.select2-drop' - fill_in 'add_quantity', with: 1 - click_link 'Add' + add_variant_to_subscription(variant3, 1) within "#sli_1" do - expect(page).to have_selector 'td.description', text: "#{product3.name} - #{variant3.full_name}" + expect(page).to have_selector '.description', text: "#{product3.name} - #{variant3.full_name}" expect(page).to have_selector 'td.price', text: "$22.00" expect(page).to have_input 'quantity', with: "1" expect(page).to have_selector 'td.total', text: "$22.00" @@ -377,9 +369,6 @@ feature 'Subscriptions' do # Total should be $29.75 expect(page).to have_selector '#order_form_total', text: "$29.75" - click_button 'Save Changes' - expect(page).to have_content "#{product3.name} - #{variant3.full_name} is not available from the selected schedule" - # Remove variant3 from the subscription within '#sli_1' do find("a.delete-item").click @@ -421,5 +410,103 @@ feature 'Subscriptions' do end end end + + describe "allowed variants" do + let!(:customer) { create(:customer, enterprise: shop, allow_charges: true) } + let!(:credit_card) { create(:credit_card, user: customer.user) } + let!(:shop_product) { create(:product, supplier: shop, distributors: [shop]) } + let!(:shop_variant) { create(:variant, product: shop_product, unit_value: "2000") } + let!(:permitted_supplier) do + create(:supplier_enterprise).tap do |supplier| + create(:enterprise_relationship, child: shop, parent: supplier, permissions_list: [:add_to_order_cycle]) + end + end + let!(:permitted_supplier_product) { create(:product, supplier: permitted_supplier, distributors: [shop]) } + let!(:permitted_supplier_variant) { create(:variant, product: permitted_supplier_product, unit_value: "2000") } + let!(:incoming_exchange_product) { create(:product, distributors: [shop]) } + let!(:incoming_exchange_variant) do + create(:variant, product: incoming_exchange_product, unit_value: "2000").tap do |variant| + create(:exchange, order_cycle: order_cycle, incoming: true, receiver: shop, variants: [variant]) + end + end + let!(:outgoing_exchange_product) { create(:product, distributors: [shop]) } + let!(:outgoing_exchange_variant) do + create(:variant, product: outgoing_exchange_product, unit_value: "2000").tap do |variant| + create(:exchange, order_cycle: order_cycle, incoming: false, receiver: shop, variants: [variant]) + end + end + let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } + let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop) } + let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } + let!(:payment_method) { create(:stripe_payment_method, distributors: [shop], preferred_enterprise_id: shop.id) } + let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } + + before do + visit admin_subscriptions_path + click_link "New Subscription" + select2_select shop.name, from: "new_subscription_shop_id" + click_button "Continue" + end + + it "permit creating and editing of the subscription" do + select2_select customer.email, from: "customer_id" + select2_select schedule.name, from: "schedule_id" + select2_select payment_method.name, from: "payment_method_id" + select2_select shipping_method.name, from: "shipping_method_id" + find_field("begins_at").click + within(".ui-datepicker-calendar") do + find(".ui-datepicker-today").click + end + click_button "Next" + + expect(page).to have_content "BILLING ADDRESS" + click_button "Next" + + # Add products + expect(page).to have_content "NAME OR SKU" + add_variant_to_subscription shop_variant, 3 + add_variant_to_subscription permitted_supplier_variant, 4 + add_variant_to_subscription incoming_exchange_variant, 5 + add_variant_to_subscription outgoing_exchange_variant, 6 + click_button "Next" + + # Submit form + expect { + click_button "Create Subscription" + expect(page).to have_current_path admin_subscriptions_path + }.to change(Subscription, :count).by(1) + + # Subscription line items are created + subscription = Subscription.last + expect(subscription.subscription_line_items.count).to eq 4 + + # Edit the subscription + visit edit_admin_subscription_path(subscription) + + # Remove shop_variant from the subscription + click_button "edit-products" + within "#sli_0" do + expect(page).to have_selector ".description", text: shop_variant.name + find("a.delete-item").click + end + + # Submit form + click_button "Save Changes" + expect(page).to have_current_path admin_subscriptions_path + + # Subscription is saved + visit edit_admin_subscription_path(subscription) + expect(page).to have_selector "#subscription-line-items .item", count: 3 + end + end + end + + def add_variant_to_subscription(variant, quantity) + row_count = all("#subscription-line-items .item").length + variant_name = variant.full_name.present? ? "#{variant.name} - #{variant.full_name}" : variant.name + select2_search variant.name, from: I18n.t(:name_or_sku), dropdown_css: ".select2-drop", select_text: variant_name + fill_in "add_quantity", with: quantity + click_link "Add" + expect(page).to have_selector("#subscription-line-items .item", count: row_count + 1) end end diff --git a/spec/lib/open_food_network/subscription_service_spec.rb b/spec/lib/open_food_network/subscription_service_spec.rb new file mode 100644 index 0000000000..7df860cf03 --- /dev/null +++ b/spec/lib/open_food_network/subscription_service_spec.rb @@ -0,0 +1,99 @@ +require "spec_helper" +require "open_food_network/subscription_service" + +module OpenFoodNetwork + describe SubscriptionService do + describe "variant eligibility for subscription" do + let!(:shop) { create(:distributor_enterprise) } + let!(:producer) { create(:supplier_enterprise) } + let!(:product) { create(:product, supplier: producer) } + let!(:variant) { product.variants.first } + + let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } + let!(:subscription) { create(:subscription, shop: shop, schedule: schedule) } + let!(:subscription_line_item) do + create(:subscription_line_item, subscription: subscription, variant: variant) + end + + let(:current_order_cycle) do + create(:simple_order_cycle, coordinator: shop, orders_open_at: 1.week.ago, + orders_close_at: 1.week.from_now) + end + + let(:future_order_cycle) do + create(:simple_order_cycle, coordinator: shop, orders_open_at: 1.week.from_now, + orders_close_at: 2.weeks.from_now) + end + + let(:past_order_cycle) do + create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.weeks.ago, + orders_close_at: 1.week.ago) + end + + let!(:order_cycle) { current_order_cycle } + + context "if the shop is the supplier for the product" do + let!(:producer) { shop } + + it "is eligible" do + expect(described_class.eligible_variants(shop)).to include(variant) + end + end + + context "if the supplier is permitted for the shop" do + let!(:enterprise_relationship) { create(:enterprise_relationship, child: shop, parent: product.supplier, permissions_list: [:add_to_order_cycle]) } + + it "is eligible" do + expect(described_class.eligible_variants(shop)).to include(variant) + end + end + + context "if the variant is involved in an exchange" do + let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop) } + let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } + + context "if it is an incoming exchange where the shop is the receiver" do + let!(:incoming_exchange) { order_cycle.exchanges.create(sender: product.supplier, receiver: shop, incoming: true, variants: [variant]) } + + it "is not eligible" do + expect(described_class.eligible_variants(shop)).to_not include(variant) + end + end + + context "if it is an outgoing exchange where the shop is the receiver" do + let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: product.supplier, receiver: shop, incoming: false, variants: [variant]) } + + context "if the order cycle is currently open" do + let!(:order_cycle) { current_order_cycle } + + it "is eligible" do + expect(described_class.eligible_variants(shop)).to include(variant) + end + end + + context "if the order cycle opens in the future" do + let!(:order_cycle) { future_order_cycle } + + it "is eligible" do + expect(described_class.eligible_variants(shop)).to include(variant) + end + end + + context "if the order cycle closed in the past" do + let!(:order_cycle) { past_order_cycle } + + it "is eligible" do + expect(described_class.eligible_variants(shop)).to include(variant) + end + end + end + end + + context "if the variant is unrelated" do + it "is not eligible" do + expect(described_class.eligible_variants(shop)).to_not include(variant) + end + end + end + end +end diff --git a/spec/services/subscription_validator_spec.rb b/spec/services/subscription_validator_spec.rb index c4878414a5..bdcd14bea5 100644 --- a/spec/services/subscription_validator_spec.rb +++ b/spec/services/subscription_validator_spec.rb @@ -1,10 +1,11 @@ require "spec_helper" describe SubscriptionValidator do - let(:shop) { instance_double(Enterprise, name: "Shop") } + let(:owner) { create(:user) } + let(:shop) { create(:enterprise, name: "Shop", owner: owner) } describe "delegation" do - let(:subscription) { create(:subscription) } + let(:subscription) { create(:subscription, shop: shop) } let(:validator) { SubscriptionValidator.new(subscription) } it "delegates to subscription" do @@ -440,6 +441,7 @@ describe SubscriptionValidator do context "but some variants are unavailable" do let(:product) { instance_double(Spree::Product, name: "some_name") } + before do allow(validator).to receive(:available_variant_ids) { [variant2.id] } allow(variant1).to receive(:product) { product } @@ -453,7 +455,9 @@ describe SubscriptionValidator do end context "and all requested variants are available" do - before { allow(validator).to receive(:available_variant_ids) { [variant1.id, variant2.id] } } + before do + allow(validator).to receive(:available_variant_ids) { [variant1.id, variant2.id] } + end it "returns true" do expect(validator.valid?).to be true @@ -463,81 +467,4 @@ describe SubscriptionValidator do end end end - - describe "variant eligibility for subscription" do - let!(:shop) { create(:distributor_enterprise) } - let!(:producer) { create(:supplier_enterprise) } - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { product.variants.first } - - let!(:order_cycle) { current_order_cycle } - let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } - let!(:subscription) { create(:subscription, shop: shop, schedule: schedule) } - let!(:subscription_line_item) do - create(:subscription_line_item, subscription: subscription, variant: variant) - end - - let(:current_order_cycle) do - create(:simple_order_cycle, coordinator: shop, orders_open_at: 1.week.ago, - orders_close_at: 1.week.from_now) - end - - let(:future_order_cycle) do - create(:simple_order_cycle, coordinator: shop, orders_open_at: 1.week.from_now, - orders_close_at: 2.weeks.from_now) - end - - let(:past_order_cycle) do - create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.weeks.ago, - orders_close_at: 1.week.ago) - end - - let(:validator) { SubscriptionValidator.new(subscription) } - - context "if it is not in an exchange" do - it "is not eligible" do - expect(validator.__send__(:available_variant_ids)).to_not include(variant.id) - end - end - - context "if it is only in an incoming exchange" do - let!(:incoming_exchange) do - create(:exchange, order_cycle: order_cycle, sender: producer, receiver: shop, - incoming: true, variants: [variant]) - end - - it "is not eligible" do - expect(validator.__send__(:available_variant_ids)).to_not include(variant.id) - end - end - - context "if is an outgoing exchange where the shop is the receiver" do - let!(:outgoing_exchange) do - create(:exchange, order_cycle: order_cycle, sender: shop, receiver: shop, - incoming: false, variants: [variant]) - end - - context "if the order cycle is currently open" do - it "is eligible" do - expect(validator.__send__(:available_variant_ids)).to include(variant.id) - end - end - - context "if the order cycle opens in the future" do - let!(:order_cycle) { future_order_cycle } - - it "is eligible" do - expect(validator.__send__(:available_variant_ids)).to include(variant.id) - end - end - - context "if the order cycle closed in the past" do - let!(:order_cycle) { past_order_cycle } - - it "is not eligible" do - expect(validator.__send__(:available_variant_ids)).to_not include(variant.id) - end - end - end - end end