diff --git a/.rspec b/.rspec index 333fedd86c..53607ea52b 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1 @@ --colour ---profile diff --git a/Gemfile b/Gemfile index b6a8f58817..e1c4d656b8 100644 --- a/Gemfile +++ b/Gemfile @@ -37,6 +37,9 @@ gem 'gmaps4rails' gem 'spinjs-rails' gem 'rack-ssl', :require => 'rack/ssl' +gem 'foreigner' +gem 'immigrant' + # Gems used only for assets and not required # in production environments by default. group :assets do diff --git a/Gemfile.lock b/Gemfile.lock index 0edc4e4190..1b762b786c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -264,6 +264,8 @@ GEM net-ssh (>= 2.1.3) nokogiri (~> 1.5) ruby-hmac + foreigner (1.6.1) + activerecord (>= 3.0.0) formatador (0.2.4) foundation-icons-sass-rails (3.0.0) railties (>= 3.1.1) @@ -301,6 +303,9 @@ GEM multi_json (~> 1.0) multi_xml (>= 0.5.2) i18n (0.6.9) + immigrant (0.1.6) + activerecord (>= 3.0) + foreigner (>= 1.2.1) journey (1.0.4) jquery-rails (2.2.2) railties (>= 3.0, < 5.0) @@ -518,6 +523,7 @@ DEPENDENCIES eaterprises_feature! factory_girl_rails faker + foreigner foundation-icons-sass-rails foundation-rails foundation_rails_helper! @@ -529,6 +535,7 @@ DEPENDENCIES guard-rspec guard-zeus haml + immigrant jquery-rails json_spec letter_opener diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index c2641615e2..35e66c49ad 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -291,7 +291,7 @@ productEditModule.controller "AdminProductEditCtrl", [ if confirm("Are you sure?") $http( method: "DELETE" - url: "/api/products/" + product.id + "/variants/" + variant.id + url: "/api/products/" + product.permalink_live + "/variants/" + variant.id + "/soft_delete" ).success (data) -> $scope.removeVariant(product, variant) diff --git a/app/controllers/api/order_cycles_controller.rb b/app/controllers/api/order_cycles_controller.rb index c32568ef92..b4b3486778 100644 --- a/app/controllers/api/order_cycles_controller.rb +++ b/app/controllers/api/order_cycles_controller.rb @@ -2,6 +2,8 @@ module Api class OrderCyclesController < Spree::Api::BaseController respond_to :json def managed + authorize! :admin, OrderCycle + authorize! :read, OrderCycle @order_cycles = OrderCycle.ransack(params[:q]).result.managed_by(current_api_user) render params[:template] || :bulk_index end @@ -12,4 +14,3 @@ module Api end end end - \ No newline at end of file diff --git a/app/controllers/spree/api/orders_controller_decorator.rb b/app/controllers/spree/api/orders_controller_decorator.rb index 53e815a5f6..ca1fc4a570 100644 --- a/app/controllers/spree/api/orders_controller_decorator.rb +++ b/app/controllers/spree/api/orders_controller_decorator.rb @@ -7,7 +7,9 @@ Spree::Api::OrdersController.class_eval do before_filter :authorize_read!, :except => [:managed] def managed + authorize! :admin, Spree::Order + authorize! :read, Spree::Order @orders = Spree::Order.ransack(params[:q]).result.distributed_by_user(current_api_user).page(params[:page]).per(params[:per_page]) respond_with(@orders, default_template: :index) end -end \ No newline at end of file +end diff --git a/app/controllers/spree/api/products_controller_decorator.rb b/app/controllers/spree/api/products_controller_decorator.rb index 7175ded8c1..0dbecaa9fd 100644 --- a/app/controllers/spree/api/products_controller_decorator.rb +++ b/app/controllers/spree/api/products_controller_decorator.rb @@ -1,5 +1,8 @@ Spree::Api::ProductsController.class_eval do def managed + authorize! :admin, Spree::Product + authorize! :read, Spree::Product + @products = product_scope.ransack(params[:q]).result.managed_by(current_api_user).page(params[:page]).per(params[:per_page]) respond_with(@products, default_template: :index) end diff --git a/app/controllers/spree/api/variants_controller_decorator.rb b/app/controllers/spree/api/variants_controller_decorator.rb new file mode 100644 index 0000000000..a225c4c401 --- /dev/null +++ b/app/controllers/spree/api/variants_controller_decorator.rb @@ -0,0 +1,13 @@ +Spree::Api::VariantsController.class_eval do + def soft_delete + @variant = scope.find(params[:variant_id]) + authorize! :delete, @variant + + @variant.deleted_at = Time.now() + if @variant.save + respond_with(@variant, :status => 204) + else + invalid_resource!(@variant) + end + end +end diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 148c18d367..f41ad76c96 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -1,5 +1,10 @@ class EnterpriseFee < ActiveRecord::Base belongs_to :enterprise + has_and_belongs_to_many :order_cycles, join_table: 'coordinator_fees' + has_many :exchange_fees, dependent: :destroy + has_many :exchanges, through: :exchange_fees + + before_destroy { order_cycles.clear } calculated_adjustments diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 122586ba96..18c3f81447 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -28,6 +28,19 @@ class Exchange < ActiveRecord::Base scope :with_product, lambda { |product| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', product.variants_including_master) } + scope :managed_by, lambda { |user| + if user.has_spree_role?('admin') + scoped + else + joins('LEFT JOIN enterprises senders ON senders.id = exchanges.sender_id'). + joins('LEFT JOIN enterprises receivers ON receivers.id = exchanges.receiver_id'). + joins('LEFT JOIN enterprise_roles sender_roles ON sender_roles.enterprise_id = senders.id'). + joins('LEFT JOIN enterprise_roles receiver_roles ON receiver_roles.enterprise_id = receivers.id'). + where('sender_roles.user_id = ? AND receiver_roles.user_id = ?', user.id, user.id) + end + } + + def clone!(new_order_cycle) exchange = self.dup exchange.order_cycle = new_order_cycle diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 96154c5a9b..f2ad8fee3e 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -84,11 +84,13 @@ class OrderCycle < ActiveRecord::Base end def suppliers - self.exchanges.incoming.map(&:sender).uniq + enterprise_ids = self.exchanges.incoming.pluck :sender_id + Enterprise.where('enterprises.id IN (?)', enterprise_ids) end def distributors - self.exchanges.outgoing.map(&:receiver).uniq + enterprise_ids = self.exchanges.outgoing.pluck :receiver_id + Enterprise.where('enterprises.id IN (?)', enterprise_ids) end def variants diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 795a818693..ee83e85bf7 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -12,11 +12,15 @@ class AbilityDecorator # Enterprise User can only access products that they are a supplier for can [:create], Spree::Product - can [:admin, :read, :update, :product_distributions, :bulk_edit, :bulk_update, :clone, :destroy], Spree::Product do |product| + can [:admin, :read, :update, :product_distributions, :bulk_edit, :bulk_update, :clone, :destroy], Spree::Product do |product| user.enterprises.include? product.supplier end - can [:admin, :index, :read, :create, :edit, :update, :search, :destroy], Spree::Variant + can [:create], Spree::Variant + can [:admin, :index, :read, :edit, :update, :search, :destroy], Spree::Variant do |variant| + user.enterprises.include? variant.product.supplier + end + can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], Spree::ProductProperty can [:admin, :index, :read, :create, :edit, :update, :destroy], Spree::Image diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 23f88b3f35..165c2d34f6 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -1,5 +1,5 @@ Spree::Variant.class_eval do - has_many :exchange_variants + has_many :exchange_variants, dependent: :destroy has_many :exchanges, through: :exchange_variants attr_accessible :unit_value, :unit_description diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml new file mode 100644 index 0000000000..4396fabcba --- /dev/null +++ b/app/views/admin/order_cycles/_row.html.haml @@ -0,0 +1,25 @@ +- order_cycle = order_cycle_form.object +- klass = "order-cycle-#{order_cycle.id} #{order_cycle_status_class order_cycle}" +%tr{class: klass} + %td= link_to order_cycle.name, main_app.edit_admin_order_cycle_path(order_cycle) + %td= order_cycle_form.text_field :orders_open_at, :class => 'datetimepicker', :value => order_cycle.orders_open_at + %td= order_cycle_form.text_field :orders_close_at, :class => 'datetimepicker', :value => order_cycle.orders_close_at + %td.suppliers + - order_cycle.suppliers.managed_by(spree_current_user).each do |s| + = s.name + %br/ + %td= order_cycle.coordinator.name + %td.distributors + - order_cycle.distributors.managed_by(spree_current_user).each do |d| + = d.name + %br/ + + %td.products + - variant_images = capture do + - order_cycle.variants.each do |v| + = image_tag(v.images.first.attachment.url(:mini)) if v.images.present? + %br/ + %span.with-tip{'data-powertip' => variant_images}= "#{order_cycle.variants.count} variants" + + %td.actions + = link_to '', main_app.clone_admin_order_cycle_path(order_cycle), class: 'clone-order-cycle icon-copy no-text' diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index 328d56d5aa..c3f4bc0b8f 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -18,6 +18,7 @@ %col %col %col + %thead %tr %th Name @@ -28,31 +29,9 @@ %th Distributors %th Products %th.actions + %tbody = f.fields_for :collection do |order_cycle_form| - - order_cycle = order_cycle_form.object - - klass = "order-cycle-#{order_cycle.id} #{order_cycle_status_class order_cycle}" - %tr{class: klass} - %td= link_to order_cycle.name, main_app.edit_admin_order_cycle_path(order_cycle) - %td= order_cycle_form.text_field :orders_open_at, :class => 'datetimepicker', :value => order_cycle.orders_open_at - %td= order_cycle_form.text_field :orders_close_at, :class => 'datetimepicker', :value => order_cycle.orders_close_at - %td.suppliers - - order_cycle.suppliers.each do |s| - = s.name - %br/ - %td= order_cycle.coordinator.name - %td.distributors - - order_cycle.distributors.each do |d| - = d.name - %br/ + = render 'admin/order_cycles/row', order_cycle_form: order_cycle_form - %td.products - - variant_images = capture do - - order_cycle.variants.each do |v| - = image_tag(v.images.first.attachment.url(:mini)) if v.images.present? - %br/ - %span.with-tip{'data-powertip' => variant_images}= "#{order_cycle.variants.count} variants" - - %td.actions - = link_to '', main_app.clone_admin_order_cycle_path(order_cycle), class: 'clone-order-cycle icon-copy no-text' = f.submit 'Update' diff --git a/app/views/admin/order_cycles/show.rep b/app/views/admin/order_cycles/show.rep index 1f91cbe3e9..04fc659813 100644 --- a/app/views/admin/order_cycles/show.rep +++ b/app/views/admin/order_cycles/show.rep @@ -9,7 +9,7 @@ r.element :order_cycle, @order_cycle do r.element :id end - r.list_of :exchanges, @order_cycle.exchanges.order('id ASC') do |exchange| + r.list_of :exchanges, @order_cycle.exchanges.managed_by(spree_current_user).order('id ASC') do |exchange| r.element :id r.element :sender_id r.element :receiver_id diff --git a/config/routes.rb b/config/routes.rb index 10a1cfd921..5f34d480b0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -108,6 +108,10 @@ Spree::Core::Engine.routes.prepend do resources :products do get :managed, on: :collection + + resources :variants do + delete :soft_delete + end end resources :orders do diff --git a/db/migrate/20140402033428_add_foreign_keys.rb b/db/migrate/20140402033428_add_foreign_keys.rb new file mode 100644 index 0000000000..f072127158 --- /dev/null +++ b/db/migrate/20140402033428_add_foreign_keys.rb @@ -0,0 +1,179 @@ +class AddForeignKeys < ActiveRecord::Migration + class AdjustmentMetadata < ActiveRecord::Base; end + class ExchangeVariant < ActiveRecord::Base; end + class Spree::InventoryUnit < ActiveRecord::Base; end + class Spree::LineItem < ActiveRecord::Base; end + class Spree::Address < ActiveRecord::Base; end + class Spree::Order < ActiveRecord::Base; end + class Spree::Taxon < ActiveRecord::Base; end + class CoordinatorFee < ActiveRecord::Base; end + + def change + setup_foreign_keys + end + + # http://stackoverflow.com/a/7679513/2720566 + def migrate(direction) + sanitise_data if direction == :up + super + end + + + private + + def sanitise_data + # Remove orphaned AdjustmentMetadata records + orphaned_adjustment_metadata = AdjustmentMetadata.joins('LEFT OUTER JOIN spree_adjustments ON spree_adjustments.id = adjustment_metadata.adjustment_id').where('spree_adjustments.id IS NULL') + say "Destroying #{orphaned_adjustment_metadata.count} orphaned AdjustmentMetadata (of total #{AdjustmentMetadata.count})" + orphaned_adjustment_metadata.destroy_all + + # Remove orphaned ExchangeVariants + orphaned_exchange_variants = ExchangeVariant.joins('LEFT OUTER JOIN spree_variants ON spree_variants.id=exchange_variants.variant_id').where('spree_variants.id IS NULL') + say "Destroying #{orphaned_exchange_variants.count} orphaned ExchangeVariants (of total #{ExchangeVariant.count})" + orphaned_exchange_variants.destroy_all + + # Remove orphaned Spree::InventoryUnits + orphaned_inventory_units = Spree::InventoryUnit.joins('LEFT OUTER JOIN spree_variants ON spree_variants.id=spree_inventory_units.variant_id').where('spree_variants.id IS NULL') + say "Destroying #{orphaned_inventory_units.count} orphaned InventoryUnits (of total #{Spree::InventoryUnit.count})" + orphaned_inventory_units.destroy_all + + # Remove orphaned Spree::LineItems + orphaned_line_items = Spree::LineItem. + joins('LEFT OUTER JOIN spree_variants ON spree_variants.id=spree_line_items.variant_id'). + joins('LEFT OUTER JOIN spree_orders ON spree_orders.id=spree_line_items.order_id'). + where('spree_variants.id IS NULL OR spree_orders.id IS NULL') + say "Destroying #{orphaned_line_items.count} orphaned LineItems (of total #{Spree::LineItem.count})" + orphaned_line_items.each { |li| li.delete } + + # Update orders without a distributor with a dummy distributor + state = Spree::State.first + country = state.andand.country + unless country && state + country = Spree::Country.create! name: 'Australia', iso_name: 'AU' + state = country.states.create! name: 'Victoria' + end + + address = Spree::Address.create!(firstname: 'Dummy distributor', lastname: 'Dummy distributor', phone: '12345678', state: state, + address1: 'Dummy distributor', city: 'Dummy distributor', zipcode: '1234', country: country) + deleted_distributor = Enterprise.create!(name: "Deleted distributor", address: address) + + orphaned_orders = Spree::Order.joins('LEFT OUTER JOIN enterprises ON enterprises.id=spree_orders.distributor_id').where('enterprises.id IS NULL') + say "Assigning a dummy distributor to #{orphaned_orders.count} orders with a deleted distributor (of total #{Spree::Order.count})" + orphaned_orders.update_all distributor_id: deleted_distributor.id + + # Remove orphaned Spree::Taxons + orphaned_taxons = Spree::Taxon.joins('LEFT OUTER JOIN spree_taxonomies ON spree_taxonomies.id=spree_taxons.taxonomy_id').where('spree_taxonomies.id IS NULL') + say "Destroying #{orphaned_taxons.count} orphaned Taxons (of total #{Spree::Taxon.count})" + orphaned_taxons.destroy_all + + # Remove orphaned CoordinatorFee records + orphaned_coordinator_fees = CoordinatorFee.joins('LEFT OUTER JOIN enterprise_fees ON enterprise_fees.id = coordinator_fees.enterprise_fee_id').where('enterprise_fees.id IS NULL') + say "Destroying #{orphaned_coordinator_fees.count} orphaned CoordinatorFees (of total #{CoordinatorFee.count})" + orphaned_coordinator_fees.each do |cf| + CoordinatorFee.connection.execute("DELETE FROM coordinator_fees WHERE coordinator_fees.order_cycle_id=#{cf.order_cycle_id} AND coordinator_fees.enterprise_fee_id=#{cf.enterprise_fee_id}") + end + end + + + def setup_foreign_keys + add_foreign_key "adjustment_metadata", "spree_adjustments", name: "adjustment_metadata_adjustment_id_fk", column: "adjustment_id" + add_foreign_key "adjustment_metadata", "enterprises", name: "adjustment_metadata_enterprise_id_fk" + add_foreign_key "carts", "spree_users", name: "carts_user_id_fk", column: "user_id" + add_foreign_key "cms_blocks", "cms_pages", name: "cms_blocks_page_id_fk", column: "page_id" + add_foreign_key "cms_categories", "cms_sites", name: "cms_categories_site_id_fk", column: "site_id", dependent: :delete + add_foreign_key "cms_categorizations", "cms_categories", name: "cms_categorizations_category_id_fk", column: "category_id" + add_foreign_key "cms_files", "cms_blocks", name: "cms_files_block_id_fk", column: "block_id" + add_foreign_key "cms_files", "cms_sites", name: "cms_files_site_id_fk", column: "site_id" + add_foreign_key "cms_layouts", "cms_layouts", name: "cms_layouts_parent_id_fk", column: "parent_id" + add_foreign_key "cms_layouts", "cms_sites", name: "cms_layouts_site_id_fk", column: "site_id", dependent: :delete + add_foreign_key "cms_pages", "cms_layouts", name: "cms_pages_layout_id_fk", column: "layout_id" + add_foreign_key "cms_pages", "cms_pages", name: "cms_pages_parent_id_fk", column: "parent_id" + add_foreign_key "cms_pages", "cms_sites", name: "cms_pages_site_id_fk", column: "site_id", dependent: :delete + add_foreign_key "cms_pages", "cms_pages", name: "cms_pages_target_page_id_fk", column: "target_page_id" + add_foreign_key "cms_snippets", "cms_sites", name: "cms_snippets_site_id_fk", column: "site_id", dependent: :delete + add_foreign_key "coordinator_fees", "enterprise_fees", name: "coordinator_fees_enterprise_fee_id_fk" + add_foreign_key "coordinator_fees", "order_cycles", name: "coordinator_fees_order_cycle_id_fk" + add_foreign_key "distributors_payment_methods", "enterprises", name: "distributors_payment_methods_distributor_id_fk", column: "distributor_id" + add_foreign_key "distributors_payment_methods", "spree_payment_methods", name: "distributors_payment_methods_payment_method_id_fk", column: "payment_method_id" + add_foreign_key "distributors_shipping_methods", "enterprises", name: "distributors_shipping_methods_distributor_id_fk", column: "distributor_id" + add_foreign_key "distributors_shipping_methods", "spree_shipping_methods", name: "distributors_shipping_methods_shipping_method_id_fk", column: "shipping_method_id" + add_foreign_key "enterprise_fees", "enterprises", name: "enterprise_fees_enterprise_id_fk" + add_foreign_key "enterprise_groups_enterprises", "enterprise_groups", name: "enterprise_groups_enterprises_enterprise_group_id_fk" + add_foreign_key "enterprise_groups_enterprises", "enterprises", name: "enterprise_groups_enterprises_enterprise_id_fk" + add_foreign_key "enterprise_roles", "enterprises", name: "enterprise_roles_enterprise_id_fk" + add_foreign_key "enterprise_roles", "spree_users", name: "enterprise_roles_user_id_fk", column: "user_id" + add_foreign_key "enterprises", "spree_addresses", name: "enterprises_address_id_fk", column: "address_id" + add_foreign_key "exchange_fees", "enterprise_fees", name: "exchange_fees_enterprise_fee_id_fk" + add_foreign_key "exchange_fees", "exchanges", name: "exchange_fees_exchange_id_fk" + add_foreign_key "exchange_variants", "exchanges", name: "exchange_variants_exchange_id_fk" + add_foreign_key "exchange_variants", "spree_variants", name: "exchange_variants_variant_id_fk", column: "variant_id" + add_foreign_key "exchanges", "order_cycles", name: "exchanges_order_cycle_id_fk" + add_foreign_key "exchanges", "enterprises", name: "exchanges_payment_enterprise_id_fk", column: "payment_enterprise_id" + add_foreign_key "exchanges", "enterprises", name: "exchanges_receiver_id_fk", column: "receiver_id" + add_foreign_key "exchanges", "enterprises", name: "exchanges_sender_id_fk", column: "sender_id" + add_foreign_key "order_cycles", "enterprises", name: "order_cycles_coordinator_id_fk", column: "coordinator_id" + add_foreign_key "product_distributions", "enterprises", name: "product_distributions_distributor_id_fk", column: "distributor_id" + add_foreign_key "product_distributions", "enterprise_fees", name: "product_distributions_enterprise_fee_id_fk" + add_foreign_key "product_distributions", "spree_products", name: "product_distributions_product_id_fk", column: "product_id" + add_foreign_key "spree_addresses", "spree_countries", name: "spree_addresses_country_id_fk", column: "country_id" + add_foreign_key "spree_addresses", "spree_states", name: "spree_addresses_state_id_fk", column: "state_id" + add_foreign_key "spree_inventory_units", "spree_orders", name: "spree_inventory_units_order_id_fk", column: "order_id" + add_foreign_key "spree_inventory_units", "spree_return_authorizations", name: "spree_inventory_units_return_authorization_id_fk", column: "return_authorization_id" + add_foreign_key "spree_inventory_units", "spree_shipments", name: "spree_inventory_units_shipment_id_fk", column: "shipment_id" + add_foreign_key "spree_inventory_units", "spree_variants", name: "spree_inventory_units_variant_id_fk", column: "variant_id" + add_foreign_key "spree_line_items", "spree_orders", name: "spree_line_items_order_id_fk", column: "order_id" + add_foreign_key "spree_line_items", "spree_variants", name: "spree_line_items_variant_id_fk", column: "variant_id" + add_foreign_key "spree_option_types_prototypes", "spree_option_types", name: "spree_option_types_prototypes_option_type_id_fk", column: "option_type_id" + add_foreign_key "spree_option_types_prototypes", "spree_prototypes", name: "spree_option_types_prototypes_prototype_id_fk", column: "prototype_id" + add_foreign_key "spree_option_values", "spree_option_types", name: "spree_option_values_option_type_id_fk", column: "option_type_id" + add_foreign_key "spree_option_values_variants", "spree_option_values", name: "spree_option_values_variants_option_value_id_fk", column: "option_value_id" + add_foreign_key "spree_option_values_variants", "spree_variants", name: "spree_option_values_variants_variant_id_fk", column: "variant_id" + add_foreign_key "spree_orders", "spree_addresses", name: "spree_orders_bill_address_id_fk", column: "bill_address_id" + add_foreign_key "spree_orders", "carts", name: "spree_orders_cart_id_fk" + add_foreign_key "spree_orders", "enterprises", name: "spree_orders_distributor_id_fk", column: "distributor_id" + add_foreign_key "spree_orders", "order_cycles", name: "spree_orders_order_cycle_id_fk" + add_foreign_key "spree_orders", "spree_addresses", name: "spree_orders_ship_address_id_fk", column: "ship_address_id" + #add_foreign_key "spree_orders", "spree_shipping_methods", name: "spree_orders_shipping_method_id_fk", column: "shipping_method_id" + add_foreign_key "spree_orders", "spree_users", name: "spree_orders_user_id_fk", column: "user_id" + add_foreign_key "spree_payments", "spree_orders", name: "spree_payments_order_id_fk", column: "order_id" + add_foreign_key "spree_payments", "spree_payment_methods", name: "spree_payments_payment_method_id_fk", column: "payment_method_id" + #add_foreign_key "spree_payments", "spree_payments", name: "spree_payments_source_id_fk", column: "source_id" + add_foreign_key "spree_prices", "spree_variants", name: "spree_prices_variant_id_fk", column: "variant_id" + add_foreign_key "spree_product_option_types", "spree_option_types", name: "spree_product_option_types_option_type_id_fk", column: "option_type_id" + add_foreign_key "spree_product_option_types", "spree_products", name: "spree_product_option_types_product_id_fk", column: "product_id" + add_foreign_key "spree_product_properties", "spree_products", name: "spree_product_properties_product_id_fk", column: "product_id" + add_foreign_key "spree_product_properties", "spree_properties", name: "spree_product_properties_property_id_fk", column: "property_id" + add_foreign_key "spree_products_promotion_rules", "spree_products", name: "spree_products_promotion_rules_product_id_fk", column: "product_id" + add_foreign_key "spree_products_promotion_rules", "spree_promotion_rules", name: "spree_products_promotion_rules_promotion_rule_id_fk", column: "promotion_rule_id" + add_foreign_key "spree_products", "spree_shipping_categories", name: "spree_products_shipping_category_id_fk", column: "shipping_category_id" + add_foreign_key "spree_products", "enterprises", name: "spree_products_supplier_id_fk", column: "supplier_id" + add_foreign_key "spree_products", "spree_tax_categories", name: "spree_products_tax_category_id_fk", column: "tax_category_id" + add_foreign_key "spree_products_taxons", "spree_products", name: "spree_products_taxons_product_id_fk", column: "product_id", dependent: :delete + add_foreign_key "spree_products_taxons", "spree_taxons", name: "spree_products_taxons_taxon_id_fk", column: "taxon_id", dependent: :delete + add_foreign_key "spree_promotion_action_line_items", "spree_promotion_actions", name: "spree_promotion_action_line_items_promotion_action_id_fk", column: "promotion_action_id" + add_foreign_key "spree_promotion_action_line_items", "spree_variants", name: "spree_promotion_action_line_items_variant_id_fk", column: "variant_id" + add_foreign_key "spree_promotion_actions", "spree_activators", name: "spree_promotion_actions_activator_id_fk", column: "activator_id" + add_foreign_key "spree_promotion_rules", "spree_activators", name: "spree_promotion_rules_activator_id_fk", column: "activator_id" + add_foreign_key "spree_properties_prototypes", "spree_properties", name: "spree_properties_prototypes_property_id_fk", column: "property_id" + add_foreign_key "spree_properties_prototypes", "spree_prototypes", name: "spree_properties_prototypes_prototype_id_fk", column: "prototype_id" + add_foreign_key "spree_return_authorizations", "spree_orders", name: "spree_return_authorizations_order_id_fk", column: "order_id" + add_foreign_key "spree_roles_users", "spree_roles", name: "spree_roles_users_role_id_fk", column: "role_id" + add_foreign_key "spree_roles_users", "spree_users", name: "spree_roles_users_user_id_fk", column: "user_id" + add_foreign_key "spree_shipments", "spree_addresses", name: "spree_shipments_address_id_fk", column: "address_id" + add_foreign_key "spree_shipments", "spree_orders", name: "spree_shipments_order_id_fk", column: "order_id" + #add_foreign_key "spree_shipments", "spree_shipping_methods", name: "spree_shipments_shipping_method_id_fk", column: "shipping_method_id" + add_foreign_key "spree_shipping_methods", "spree_shipping_categories", name: "spree_shipping_methods_shipping_category_id_fk", column: "shipping_category_id" + add_foreign_key "spree_shipping_methods", "spree_zones", name: "spree_shipping_methods_zone_id_fk", column: "zone_id" + add_foreign_key "spree_state_changes", "spree_users", name: "spree_state_changes_user_id_fk", column: "user_id" + add_foreign_key "spree_states", "spree_countries", name: "spree_states_country_id_fk", column: "country_id" + add_foreign_key "spree_tax_rates", "spree_tax_categories", name: "spree_tax_rates_tax_category_id_fk", column: "tax_category_id" + add_foreign_key "spree_tax_rates", "spree_zones", name: "spree_tax_rates_zone_id_fk", column: "zone_id" + add_foreign_key "spree_taxons", "spree_taxons", name: "spree_taxons_parent_id_fk", column: "parent_id" + add_foreign_key "spree_taxons", "spree_taxonomies", name: "spree_taxons_taxonomy_id_fk", column: "taxonomy_id" + add_foreign_key "spree_users", "spree_addresses", name: "spree_users_bill_address_id_fk", column: "bill_address_id" + add_foreign_key "spree_users", "spree_addresses", name: "spree_users_ship_address_id_fk", column: "ship_address_id" + add_foreign_key "spree_variants", "spree_products", name: "spree_variants_product_id_fk", column: "product_id" + add_foreign_key "spree_zone_members", "spree_zones", name: "spree_zone_members_zone_id_fk", column: "zone_id" + add_foreign_key "suburbs", "spree_states", name: "suburbs_state_id_fk", column: "state_id" + end +end diff --git a/db/schema.rb b/db/schema.rb index e7147d24b5..359326e90f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -967,4 +967,151 @@ ActiveRecord::Schema.define(:version => 20140425055718) do t.integer "state_id" end + add_foreign_key "adjustment_metadata", "enterprises", name: "adjustment_metadata_enterprise_id_fk" + add_foreign_key "adjustment_metadata", "spree_adjustments", name: "adjustment_metadata_adjustment_id_fk", column: "adjustment_id" + + add_foreign_key "carts", "spree_users", name: "carts_user_id_fk", column: "user_id" + + add_foreign_key "cms_blocks", "cms_pages", name: "cms_blocks_page_id_fk", column: "page_id" + + add_foreign_key "cms_categories", "cms_sites", name: "cms_categories_site_id_fk", column: "site_id", dependent: :delete + + add_foreign_key "cms_categorizations", "cms_categories", name: "cms_categorizations_category_id_fk", column: "category_id" + + add_foreign_key "cms_files", "cms_blocks", name: "cms_files_block_id_fk", column: "block_id" + add_foreign_key "cms_files", "cms_sites", name: "cms_files_site_id_fk", column: "site_id" + + add_foreign_key "cms_layouts", "cms_layouts", name: "cms_layouts_parent_id_fk", column: "parent_id" + add_foreign_key "cms_layouts", "cms_sites", name: "cms_layouts_site_id_fk", column: "site_id", dependent: :delete + + add_foreign_key "cms_pages", "cms_layouts", name: "cms_pages_layout_id_fk", column: "layout_id" + add_foreign_key "cms_pages", "cms_pages", name: "cms_pages_parent_id_fk", column: "parent_id" + add_foreign_key "cms_pages", "cms_pages", name: "cms_pages_target_page_id_fk", column: "target_page_id" + add_foreign_key "cms_pages", "cms_sites", name: "cms_pages_site_id_fk", column: "site_id", dependent: :delete + + add_foreign_key "cms_snippets", "cms_sites", name: "cms_snippets_site_id_fk", column: "site_id", dependent: :delete + + add_foreign_key "coordinator_fees", "enterprise_fees", name: "coordinator_fees_enterprise_fee_id_fk" + add_foreign_key "coordinator_fees", "order_cycles", name: "coordinator_fees_order_cycle_id_fk" + + add_foreign_key "distributors_payment_methods", "enterprises", name: "distributors_payment_methods_distributor_id_fk", column: "distributor_id" + add_foreign_key "distributors_payment_methods", "spree_payment_methods", name: "distributors_payment_methods_payment_method_id_fk", column: "payment_method_id" + + add_foreign_key "distributors_shipping_methods", "enterprises", name: "distributors_shipping_methods_distributor_id_fk", column: "distributor_id" + add_foreign_key "distributors_shipping_methods", "spree_shipping_methods", name: "distributors_shipping_methods_shipping_method_id_fk", column: "shipping_method_id" + + add_foreign_key "enterprise_fees", "enterprises", name: "enterprise_fees_enterprise_id_fk" + + add_foreign_key "enterprise_groups_enterprises", "enterprise_groups", name: "enterprise_groups_enterprises_enterprise_group_id_fk" + add_foreign_key "enterprise_groups_enterprises", "enterprises", name: "enterprise_groups_enterprises_enterprise_id_fk" + + add_foreign_key "enterprise_roles", "enterprises", name: "enterprise_roles_enterprise_id_fk" + add_foreign_key "enterprise_roles", "spree_users", name: "enterprise_roles_user_id_fk", column: "user_id" + + add_foreign_key "enterprises", "spree_addresses", name: "enterprises_address_id_fk", column: "address_id" + + add_foreign_key "exchange_fees", "enterprise_fees", name: "exchange_fees_enterprise_fee_id_fk" + add_foreign_key "exchange_fees", "exchanges", name: "exchange_fees_exchange_id_fk" + + add_foreign_key "exchange_variants", "exchanges", name: "exchange_variants_exchange_id_fk" + add_foreign_key "exchange_variants", "spree_variants", name: "exchange_variants_variant_id_fk", column: "variant_id" + + add_foreign_key "exchanges", "enterprises", name: "exchanges_payment_enterprise_id_fk", column: "payment_enterprise_id" + add_foreign_key "exchanges", "enterprises", name: "exchanges_receiver_id_fk", column: "receiver_id" + add_foreign_key "exchanges", "enterprises", name: "exchanges_sender_id_fk", column: "sender_id" + add_foreign_key "exchanges", "order_cycles", name: "exchanges_order_cycle_id_fk" + + add_foreign_key "order_cycles", "enterprises", name: "order_cycles_coordinator_id_fk", column: "coordinator_id" + + add_foreign_key "product_distributions", "enterprise_fees", name: "product_distributions_enterprise_fee_id_fk" + add_foreign_key "product_distributions", "enterprises", name: "product_distributions_distributor_id_fk", column: "distributor_id" + add_foreign_key "product_distributions", "spree_products", name: "product_distributions_product_id_fk", column: "product_id" + + add_foreign_key "spree_addresses", "spree_countries", name: "spree_addresses_country_id_fk", column: "country_id" + add_foreign_key "spree_addresses", "spree_states", name: "spree_addresses_state_id_fk", column: "state_id" + + add_foreign_key "spree_inventory_units", "spree_orders", name: "spree_inventory_units_order_id_fk", column: "order_id" + add_foreign_key "spree_inventory_units", "spree_return_authorizations", name: "spree_inventory_units_return_authorization_id_fk", column: "return_authorization_id" + add_foreign_key "spree_inventory_units", "spree_shipments", name: "spree_inventory_units_shipment_id_fk", column: "shipment_id" + add_foreign_key "spree_inventory_units", "spree_variants", name: "spree_inventory_units_variant_id_fk", column: "variant_id" + + add_foreign_key "spree_line_items", "spree_orders", name: "spree_line_items_order_id_fk", column: "order_id" + add_foreign_key "spree_line_items", "spree_variants", name: "spree_line_items_variant_id_fk", column: "variant_id" + + add_foreign_key "spree_option_types_prototypes", "spree_option_types", name: "spree_option_types_prototypes_option_type_id_fk", column: "option_type_id" + add_foreign_key "spree_option_types_prototypes", "spree_prototypes", name: "spree_option_types_prototypes_prototype_id_fk", column: "prototype_id" + + add_foreign_key "spree_option_values", "spree_option_types", name: "spree_option_values_option_type_id_fk", column: "option_type_id" + + add_foreign_key "spree_option_values_variants", "spree_option_values", name: "spree_option_values_variants_option_value_id_fk", column: "option_value_id" + add_foreign_key "spree_option_values_variants", "spree_variants", name: "spree_option_values_variants_variant_id_fk", column: "variant_id" + + add_foreign_key "spree_orders", "carts", name: "spree_orders_cart_id_fk" + add_foreign_key "spree_orders", "enterprises", name: "spree_orders_distributor_id_fk", column: "distributor_id" + add_foreign_key "spree_orders", "order_cycles", name: "spree_orders_order_cycle_id_fk" + add_foreign_key "spree_orders", "spree_addresses", name: "spree_orders_bill_address_id_fk", column: "bill_address_id" + add_foreign_key "spree_orders", "spree_addresses", name: "spree_orders_ship_address_id_fk", column: "ship_address_id" + add_foreign_key "spree_orders", "spree_users", name: "spree_orders_user_id_fk", column: "user_id" + + add_foreign_key "spree_payments", "spree_orders", name: "spree_payments_order_id_fk", column: "order_id" + add_foreign_key "spree_payments", "spree_payment_methods", name: "spree_payments_payment_method_id_fk", column: "payment_method_id" + + add_foreign_key "spree_prices", "spree_variants", name: "spree_prices_variant_id_fk", column: "variant_id" + + add_foreign_key "spree_product_option_types", "spree_option_types", name: "spree_product_option_types_option_type_id_fk", column: "option_type_id" + add_foreign_key "spree_product_option_types", "spree_products", name: "spree_product_option_types_product_id_fk", column: "product_id" + + add_foreign_key "spree_product_properties", "spree_products", name: "spree_product_properties_product_id_fk", column: "product_id" + add_foreign_key "spree_product_properties", "spree_properties", name: "spree_product_properties_property_id_fk", column: "property_id" + + add_foreign_key "spree_products", "enterprises", name: "spree_products_supplier_id_fk", column: "supplier_id" + add_foreign_key "spree_products", "spree_shipping_categories", name: "spree_products_shipping_category_id_fk", column: "shipping_category_id" + add_foreign_key "spree_products", "spree_tax_categories", name: "spree_products_tax_category_id_fk", column: "tax_category_id" + + add_foreign_key "spree_products_promotion_rules", "spree_products", name: "spree_products_promotion_rules_product_id_fk", column: "product_id" + add_foreign_key "spree_products_promotion_rules", "spree_promotion_rules", name: "spree_products_promotion_rules_promotion_rule_id_fk", column: "promotion_rule_id" + + add_foreign_key "spree_products_taxons", "spree_products", name: "spree_products_taxons_product_id_fk", column: "product_id", dependent: :delete + add_foreign_key "spree_products_taxons", "spree_taxons", name: "spree_products_taxons_taxon_id_fk", column: "taxon_id", dependent: :delete + + add_foreign_key "spree_promotion_action_line_items", "spree_promotion_actions", name: "spree_promotion_action_line_items_promotion_action_id_fk", column: "promotion_action_id" + add_foreign_key "spree_promotion_action_line_items", "spree_variants", name: "spree_promotion_action_line_items_variant_id_fk", column: "variant_id" + + add_foreign_key "spree_promotion_actions", "spree_activators", name: "spree_promotion_actions_activator_id_fk", column: "activator_id" + + add_foreign_key "spree_promotion_rules", "spree_activators", name: "spree_promotion_rules_activator_id_fk", column: "activator_id" + + add_foreign_key "spree_properties_prototypes", "spree_properties", name: "spree_properties_prototypes_property_id_fk", column: "property_id" + add_foreign_key "spree_properties_prototypes", "spree_prototypes", name: "spree_properties_prototypes_prototype_id_fk", column: "prototype_id" + + add_foreign_key "spree_return_authorizations", "spree_orders", name: "spree_return_authorizations_order_id_fk", column: "order_id" + + add_foreign_key "spree_roles_users", "spree_roles", name: "spree_roles_users_role_id_fk", column: "role_id" + add_foreign_key "spree_roles_users", "spree_users", name: "spree_roles_users_user_id_fk", column: "user_id" + + add_foreign_key "spree_shipments", "spree_addresses", name: "spree_shipments_address_id_fk", column: "address_id" + add_foreign_key "spree_shipments", "spree_orders", name: "spree_shipments_order_id_fk", column: "order_id" + + add_foreign_key "spree_shipping_methods", "spree_shipping_categories", name: "spree_shipping_methods_shipping_category_id_fk", column: "shipping_category_id" + add_foreign_key "spree_shipping_methods", "spree_zones", name: "spree_shipping_methods_zone_id_fk", column: "zone_id" + + add_foreign_key "spree_state_changes", "spree_users", name: "spree_state_changes_user_id_fk", column: "user_id" + + add_foreign_key "spree_states", "spree_countries", name: "spree_states_country_id_fk", column: "country_id" + + add_foreign_key "spree_tax_rates", "spree_tax_categories", name: "spree_tax_rates_tax_category_id_fk", column: "tax_category_id" + add_foreign_key "spree_tax_rates", "spree_zones", name: "spree_tax_rates_zone_id_fk", column: "zone_id" + + add_foreign_key "spree_taxons", "spree_taxonomies", name: "spree_taxons_taxonomy_id_fk", column: "taxonomy_id" + add_foreign_key "spree_taxons", "spree_taxons", name: "spree_taxons_parent_id_fk", column: "parent_id" + + add_foreign_key "spree_users", "spree_addresses", name: "spree_users_bill_address_id_fk", column: "bill_address_id" + add_foreign_key "spree_users", "spree_addresses", name: "spree_users_ship_address_id_fk", column: "ship_address_id" + + add_foreign_key "spree_variants", "spree_products", name: "spree_variants_product_id_fk", column: "product_id" + + add_foreign_key "spree_zone_members", "spree_zones", name: "spree_zone_members_zone_id_fk", column: "zone_id" + + add_foreign_key "suburbs", "spree_states", name: "suburbs_state_id_fk", column: "state_id" + end diff --git a/lib/spree/api/testing_support/helpers_decorator.rb b/lib/spree/api/testing_support/helpers_decorator.rb new file mode 100644 index 0000000000..0ade798960 --- /dev/null +++ b/lib/spree/api/testing_support/helpers_decorator.rb @@ -0,0 +1,7 @@ +require 'spree/api/testing_support/helpers' + +Spree::Api::TestingSupport::Helpers.class_eval do + def current_api_user + @current_api_user ||= stub_model(Spree::LegacyUser, :email => "spree@example.com", :enterprises => []) + end +end diff --git a/lib/spree/api/testing_support/setup.rb b/lib/spree/api/testing_support/setup.rb new file mode 100644 index 0000000000..cbf8393e33 --- /dev/null +++ b/lib/spree/api/testing_support/setup.rb @@ -0,0 +1,43 @@ +module Spree + module Api + module TestingSupport + module Setup + def sign_in_as_user! + let!(:current_api_user) do + user = stub_model(Spree::LegacyUser) + user.stub(:has_spree_role?).with("admin").and_return(false) + user.stub(:enterprises) { [] } + user + end + end + + # enterprises is an array of variable names of let defines + # eg. + # let(:enterprise) { ... } + # sign_in_as_enterprise_user! [:enterprise] + def sign_in_as_enterprise_user!(enterprises) + let!(:current_api_user) do + user = create(:user) + user.spree_roles = [] + enterprises.each { |e| user.enterprise_roles.create(enterprise: send(e)) } + user.save! + user + end + end + + + def sign_in_as_admin! + let!(:current_api_user) do + user = stub_model(Spree::LegacyUser) + user.stub(:has_spree_role?).with("admin").and_return(true) + + # Stub enterprises, needed for cancan ability checks + user.stub(:enterprises) { [] } + + user + end + end + end + end + end +end diff --git a/spec/controllers/api/order_cycles_controller_spec.rb b/spec/controllers/api/order_cycles_controller_spec.rb index 55a6ea096e..7f8bbec7af 100644 --- a/spec/controllers/api/order_cycles_controller_spec.rb +++ b/spec/controllers/api/order_cycles_controller_spec.rb @@ -6,15 +6,37 @@ module Api include Spree::Api::TestingSupport::Helpers render_views - context "as a normal user" do - let!(:oc1) { FactoryGirl.create(:order_cycle) } - let!(:oc2) { FactoryGirl.create(:order_cycle) } - let(:attributes) { [:id, :name, :suppliers, :distributors] } + let!(:oc1) { FactoryGirl.create(:order_cycle) } + let!(:oc2) { FactoryGirl.create(:order_cycle) } + let(:coordinator) { oc1.coordinator } + let(:attributes) { [:id, :name, :suppliers, :distributors] } - before do - stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => current_api_user + before do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => current_api_user + end + + context "as a normal user" do + sign_in_as_user! + + it "should deny me access to managed order cycles" do + spree_get :managed, { :format => :json } + assert_unauthorized! end + end + + context "as an enterprise user" do + sign_in_as_enterprise_user! [:coordinator] + + it "retrieves a list of variants with appropriate attributes" do + get :managed, { :format => :json } + keys = json_response.first.keys.map{ |key| key.to_sym } + attributes.all?{ |attr| keys.include? attr }.should == true + end + end + + context "as an administrator" do + sign_in_as_admin! it "retrieves a list of variants with appropriate attributes" do get :managed, { :format => :json } @@ -89,4 +111,4 @@ module Api end end end -end \ No newline at end of file +end diff --git a/spec/controllers/spree/api/orders_controller_spec.rb b/spec/controllers/spree/api/orders_controller_spec.rb index bc34ba5f39..ec87169f76 100644 --- a/spec/controllers/spree/api/orders_controller_spec.rb +++ b/spec/controllers/spree/api/orders_controller_spec.rb @@ -5,7 +5,15 @@ module Spree describe Spree::Api::OrdersController do include Spree::Api::TestingSupport::Helpers render_views - context "as a normal user" do + + before do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => current_api_user + end + + let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :line_items, :distributor, :order_cycle, :number] } + + def self.make_simple_data! let!(:dist1) { FactoryGirl.create(:distributor_enterprise) } let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } @@ -14,13 +22,23 @@ module Spree let!(:line_item2) { FactoryGirl.create(:line_item, order: order2) } let!(:line_item3) { FactoryGirl.create(:line_item, order: order2) } let!(:line_item4) { FactoryGirl.create(:line_item, order: order3) } - let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :line_items, :distributor, :order_cycle, :number] } let(:line_item_attributes) { [:id, :quantity, :max_quantity, :supplier, :units_product, :units_variant] } - - before do - stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => current_api_user + end + + + context "as a normal user" do + sign_in_as_user! + make_simple_data! + + it "should deny me access to managed orders" do + spree_get :managed, { :template => 'bulk_index', :format => :json } + assert_unauthorized! end + end + + context "as an administrator" do + sign_in_as_admin! + make_simple_data! before :each do spree_get :managed, { :template => 'bulk_index', :format => :json } @@ -68,7 +86,7 @@ module Spree end end - context "As an enterprise user" do + context "as an enterprise user" do let(:supplier) { create(:supplier_enterprise) } let(:distributor1) { create(:distributor_enterprise) } let(:distributor2) { create(:distributor_enterprise) } @@ -77,51 +95,35 @@ module Spree let!(:line_item2) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: distributor2, billing_address: FactoryGirl.create(:address) ) } let!(:line_item3) { FactoryGirl.create(:line_item, order: order2, product: FactoryGirl.create(:product, supplier: supplier)) } - let(:supplier_user) do - user = create(:user) - user.spree_roles = [] - user.enterprise_roles.create(enterprise: supplier) - user.save! - user - end - let(:distributor1_user) do - user = create(:user) - user.spree_roles = [] - user.enterprise_roles.create(enterprise: distributor1) - user.save! - user - end - let(:distributor2_user) do - user = create(:user) - user.spree_roles = [] - user.enterprise_roles.create(enterprise: distributor2) - user.save! - user - end context "producer enterprise" do + sign_in_as_enterprise_user! [:supplier] + before :each do - stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => supplier_user spree_get :managed, { :template => 'bulk_index', :format => :json } end it "does not display line item for which my enteprise is a supplier" do - json_response.map{ |order| order['line_items'] }.flatten.length.should == 0 + response.status.should == 401 end end context "hub enterprise" do + sign_in_as_enterprise_user! [:distributor1] + before :each do - stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => distributor1_user spree_get :managed, { :template => 'bulk_index', :format => :json } end + it "retrieves a list of orders" do + keys = json_response.first.keys.map{ |key| key.to_sym } + order_attributes.all?{ |attr| keys.include? attr }.should == true + end + it "only displays line items from orders for which my enterprise is a distributor" do json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.should == [line_item1.id, line_item2.id] end end end end -end \ No newline at end of file +end diff --git a/spec/controllers/spree/api/products_controller_spec.rb b/spec/controllers/spree/api/products_controller_spec.rb index 0d7560aa1b..c64b95853f 100644 --- a/spec/controllers/spree/api/products_controller_spec.rb +++ b/spec/controllers/spree/api/products_controller_spec.rb @@ -6,9 +6,10 @@ module Spree include Spree::Api::TestingSupport::Helpers render_views - let!(:product1) { FactoryGirl.create(:product) } - let!(:product2) { FactoryGirl.create(:product) } - let!(:product3) { FactoryGirl.create(:product) } + let(:supplier) { FactoryGirl.create(:supplier_enterprise) } + let!(:product1) { FactoryGirl.create(:product, supplier: supplier) } + let!(:product2) { FactoryGirl.create(:product, supplier: supplier) } + let!(:product3) { FactoryGirl.create(:product, supplier: supplier) } let(:attributes) { [:id, :name, :supplier, :price, :on_hand, :available_on, :permalink_live] } let(:unit_attributes) { [:id, :name, :group_buy_unit_size, :variant_unit] } @@ -18,6 +19,37 @@ module Spree end context "as a normal user" do + sign_in_as_user! + + it "should deny me access to managed products" do + spree_get :managed, { :template => 'bulk_index', :format => :json } + assert_unauthorized! + end + end + + context "as an enterprise user" do + sign_in_as_enterprise_user! [:supplier] + + before :each do + spree_get :index, { :template => 'bulk_index', :format => :json } + end + + it "retrieves a list of managed products" do + spree_get :managed, { :template => 'bulk_index', :format => :json } + keys = json_response.first.keys.map{ |key| key.to_sym } + attributes.all?{ |attr| keys.include? attr }.should == true + end + end + + context "as an administrator" do + sign_in_as_admin! + + it "retrieves a list of managed products" do + spree_get :managed, { :template => 'bulk_index', :format => :json } + keys = json_response.first.keys.map{ |key| key.to_sym } + attributes.all?{ |attr| keys.include? attr }.should == true + end + it "retrieves a list of products with appropriate attributes" do spree_get :index, { :template => 'bulk_index', :format => :json } keys = json_response.first.keys.map{ |key| key.to_sym } @@ -61,4 +93,4 @@ module Spree end end end -end \ No newline at end of file +end diff --git a/spec/controllers/spree/api/variants_controller_spec.rb b/spec/controllers/spree/api/variants_controller_spec.rb index c61928e1bb..818e10457a 100644 --- a/spec/controllers/spree/api/variants_controller_spec.rb +++ b/spec/controllers/spree/api/variants_controller_spec.rb @@ -1,11 +1,10 @@ require 'spec_helper' -require 'spree/api/testing_support/helpers' module Spree describe Spree::Api::VariantsController do - include Spree::Api::TestingSupport::Helpers render_views - + + let(:supplier) { FactoryGirl.create(:supplier_enterprise) } let!(:variant1) { FactoryGirl.create(:variant) } let!(:variant2) { FactoryGirl.create(:variant) } let!(:variant3) { FactoryGirl.create(:variant) } @@ -18,6 +17,8 @@ module Spree end context "as a normal user" do + sign_in_as_user! + it "retrieves a list of variants with appropriate attributes" do spree_get :index, { :template => 'bulk_index', :format => :json } keys = json_response.first.keys.map{ |key| key.to_sym } @@ -29,12 +30,53 @@ module Spree keys = json_response.keys.map{ |key| key.to_sym } unit_attributes.all?{ |attr| keys.include? attr }.should == true end - #it "sorts variants in ascending id order" do - # spree_get :index, { :template => 'bulk_index', :format => :json } - # ids = json_response.map{ |variant| variant['id'] } - # ids[0].should < ids[1] - # ids[1].should < ids[2] - #end + + it "is denied access when trying to delete a variant" do + product = create(:product) + variant = product.master + + spree_delete :soft_delete, {variant_id: variant.to_param, product_id: product.to_param, format: :json} + assert_unauthorized! + lambda { variant.reload }.should_not raise_error + variant.deleted_at.should be_nil + end + end + + context "as an enterprise user" do + sign_in_as_enterprise_user! [:supplier] + let(:supplier_other) { create(:supplier_enterprise) } + let(:product) { create(:product, supplier: supplier) } + let(:variant) { product.master } + let(:product_other) { create(:product, supplier: supplier_other) } + let(:variant_other) { product_other.master } + + it "soft deletes a variant" do + spree_delete :soft_delete, {variant_id: variant.to_param, product_id: product.to_param, format: :json} + response.status.should == 204 + lambda { variant.reload }.should_not raise_error + variant.deleted_at.should_not be_nil + end + + it "is denied access to soft deleting another enterprises' variant" do + spree_delete :soft_delete, {variant_id: variant_other.to_param, product_id: product_other.to_param, format: :json} + assert_unauthorized! + lambda { variant.reload }.should_not raise_error + variant.deleted_at.should be_nil + end + end + + context "as an administrator" do + sign_in_as_admin! + + it "soft deletes a variant" do + product = create(:product) + variant = product.master + + spree_delete :soft_delete, {variant_id: variant.to_param, product_id: product.to_param, format: :json} + response.status.should == 204 + lambda { variant.reload }.should_not raise_error + variant.deleted_at.should_not be_nil + end end end -end \ No newline at end of file +end diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index 1a227bc6bd..a56f669756 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -9,7 +9,7 @@ feature %q{ before :all do @default_wait_time = Capybara.default_wait_time - Capybara.default_wait_time = 5 + Capybara.default_wait_time = 10 end after :all do diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index 62d9b53cee..0c8d81bd5c 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -609,7 +609,7 @@ feature %q{ visit '/admin/products/bulk_edit' page.should have_selector "a.view-variants" - all("a.view-variants").each{ |e| e.click } + all("a.view-variants").each { |e| e.click } page.should have_selector "a.delete-variant", :count => 3 @@ -620,7 +620,7 @@ feature %q{ visit '/admin/products/bulk_edit' page.should have_selector "a.view-variants" - all("a.view-variants").select{ |e| e.visible? }.each{ |e| e.click } + all("a.view-variants").select { |e| e.visible? }.each { |e| e.click } page.should have_selector "a.delete-variant", :count => 2 end diff --git a/spec/features/admin/cms_spec.rb b/spec/features/admin/cms_spec.rb index f032994a9d..a01ce78101 100644 --- a/spec/features/admin/cms_spec.rb +++ b/spec/features/admin/cms_spec.rb @@ -8,7 +8,6 @@ feature %q{ include AuthenticationWorkflow include WebHelper - scenario "admin can access CMS admin and return to Spree admin" do login_to_admin_section click_link 'Configuration' @@ -16,7 +15,7 @@ feature %q{ page.should have_content "ComfortableMexicanSofa" click_link 'Spree Admin' - current_path.should == spree.admin_orders_path + current_path.should match(/^\/admin/) end scenario "anonymous user can't access CMS admin" do diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index fdd5c009d4..2eff8508c7 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -9,7 +9,7 @@ feature %q{ before :all do @orig_default_wait_time = Capybara.default_wait_time - Capybara.default_wait_time = 5 + Capybara.default_wait_time = 10 end after :all do @@ -217,11 +217,11 @@ feature %q{ end # And the distributors should have fees - distributor = oc.distributors.sort_by(&:name).first + distributor = oc.distributors.sort_by(&:id).first page.should have_select 'order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_id', selected: distributor.name page.should have_select 'order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_fee_id', selected: distributor.enterprise_fees.first.name - distributor = oc.distributors.sort_by(&:name).last + distributor = oc.distributors.sort_by(&:id).last page.should have_select 'order_cycle_outgoing_exchange_1_enterprise_fees_0_enterprise_id', selected: distributor.name page.should have_select 'order_cycle_outgoing_exchange_1_enterprise_fees_0_enterprise_fee_id', selected: distributor.enterprise_fees.first.name end @@ -295,6 +295,7 @@ feature %q{ click_button 'Add supplier' page.all("table.exchanges tr.supplier td.products input").each { |e| e.click } + wait_until { page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).present? } page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).click # uncheck (with visible:true filter) check "order_cycle_incoming_exchange_2_variants_#{v1.id}" check "order_cycle_incoming_exchange_2_variants_#{v2.id}" @@ -462,17 +463,22 @@ feature %q{ login_to_admin_as @new_user end - scenario "can view products I am coordinating" do - oc_user_coordinating = create(:simple_order_cycle, { coordinator: supplier1, name: 'Order Cycle 1' } ) + scenario "viewing a list of order cycles I am coordinating" do + oc_user_coordinating = create(:simple_order_cycle, { suppliers: [supplier1, supplier2], coordinator: supplier1, distributors: [distributor1, distributor2], name: 'Order Cycle 1' } ) oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier2, name: 'Order Cycle 2' } ) click_link "Order Cycles" + # I should see only the order cycle I am coordinating page.should have_content oc_user_coordinating.name page.should_not have_content oc_for_other_user.name + + # The order cycle should not show enterprises that I don't manage + page.should_not have_selector 'td.suppliers', text: supplier2.name + page.should_not have_selector 'td.distributors', text: distributor2.name end - scenario "can create a new order cycle" do + scenario "creating a new order cycle" do click_link "Order Cycles" click_link 'New Order Cycle' @@ -510,6 +516,16 @@ feature %q{ order_cycle.coordinator.should == distributor1 end + scenario "editing an order cycle" do + oc = create(:simple_order_cycle, { suppliers: [supplier1, supplier2], coordinator: supplier1, distributors: [distributor1, distributor2], name: 'Order Cycle 1' } ) + + visit edit_admin_order_cycle_path(oc) + + # I should not see exchanges for supplier2 or distributor2 + page.all('tr.supplier').count.should == 1 + page.all('tr.distributor').count.should == 1 + end + scenario "cloning an order cycle" do oc = create(:simple_order_cycle) diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index 58a5ce1941..7a452b213a 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -1100,7 +1100,7 @@ describe "AdminProductEditCtrl", -> describe "when the variant has been saved", -> - it "deletes variants with a http delete request to /api/products/product_id/variants/(variant_id)", -> + it "deletes variants with a http delete request to /api/products/product_permalink/variants/(variant_id)", -> spyOn(window, "confirm").andReturn true scope.products = [ { @@ -1117,7 +1117,7 @@ describe "AdminProductEditCtrl", -> } ] scope.dirtyProducts = {} - httpBackend.expectDELETE("/api/products/9/variants/3").respond 200, "data" + httpBackend.expectDELETE("/api/products/apples/variants/3/soft_delete").respond 200, "data" scope.deleteVariant scope.products[0], scope.products[0].variants[0] httpBackend.flush() @@ -1159,7 +1159,7 @@ describe "AdminProductEditCtrl", -> id: 13 name: "P1" - httpBackend.expectDELETE("/api/products/9/variants/3").respond 200, "data" + httpBackend.expectDELETE("/api/products/apples/variants/3/soft_delete").respond 200, "data" scope.deleteVariant scope.products[0], scope.products[0].variants[0] httpBackend.flush() expect(scope.products[0].variants).toEqual [ diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb index 806eb7759c..fc72f383d2 100644 --- a/spec/models/enterprise_fee_spec.rb +++ b/spec/models/enterprise_fee_spec.rb @@ -9,6 +9,25 @@ describe EnterpriseFee do it { should validate_presence_of(:name) } end + describe "callbacks" do + it "removes itself from order cycle coordinator fees when destroyed" do + ef = create(:enterprise_fee) + oc = create(:simple_order_cycle, coordinator_fees: [ef]) + + ef.destroy + oc.reload.coordinator_fee_ids.should be_empty + end + + it "removes itself from order cycle exchange fees when destroyed" do + ef = create(:enterprise_fee) + oc = create(:simple_order_cycle) + ex = create(:exchange, order_cycle: oc, enterprise_fees: [ef]) + + ef.destroy + ex.reload.exchange_fee_ids.should be_empty + end + end + describe "scopes" do describe "finding per-item enterprise fees" do it "does not return fees with FlatRate and FlexiRate calculators" do diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index 9614a4d357..ed6cdbfc93 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -85,6 +85,43 @@ describe Exchange do let(:distributor) { create(:distributor_enterprise) } let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } + describe "finding exchanges managed by a particular user" do + let(:user) do + user = create(:user) + user.spree_roles = [] + user + end + + before { Exchange.destroy_all } + + it "returns exchanges where the user manages both the sender and the receiver" do + exchange = create(:exchange, order_cycle: oc) + exchange.sender.users << user + exchange.receiver.users << user + + Exchange.managed_by(user).should == [exchange] + end + + it "does not return exchanges where the user manages only the sender" do + exchange = create(:exchange, order_cycle: oc) + exchange.sender.users << user + + Exchange.managed_by(user).should be_empty + end + + it "does not return exchanges where the user manages only the receiver" do + exchange = create(:exchange, order_cycle: oc) + exchange.receiver.users << user + + Exchange.managed_by(user).should be_empty + end + + it "does not return exchanges where the user manages neither enterprise" do + exchange = create(:exchange, order_cycle: oc) + Exchange.managed_by(user).should be_empty + end + end + it "finds exchanges in a particular order cycle" do ex = create(:exchange, order_cycle: oc) Exchange.in_order_cycle(oc).should == [ex] diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index e3d7581727..fc13925bae 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -48,7 +48,12 @@ module Spree end it "should be able to read/write their enterprises' product variants" do - should have_ability([:admin, :index, :read, :create, :edit, :search, :update, :destroy], for: Spree::Variant) + should have_ability([:create], for: Spree::Variant) + should have_ability([:admin, :index, :read, :create, :edit, :search, :update, :destroy], for: p1.master) + end + + it "should not be able to read/write other enterprises' product variants" do + should_not have_ability([:admin, :index, :read, :create, :edit, :search, :update, :destroy], for: p2.master) end it "should be able to read/write their enterprises' product properties" do diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 9c37663e1e..4b9e1fab0a 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -374,4 +374,14 @@ module Spree end end end + + describe "destruction" do + it "destroys exchange variants" do + v = create(:variant) + e = create(:exchange, variants: [v]) + + v.destroy + e.reload.variant_ids.should be_empty + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 76d72d7291..6cf9229252 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -23,6 +23,9 @@ WebMock.disable_net_connect!(:allow_localhost => true) Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} require 'spree/core/testing_support/controller_requests' require 'spree/core/testing_support/capybara_ext' +require 'spree/api/testing_support/setup' +require 'spree/api/testing_support/helpers' +require 'spree/api/testing_support/helpers_decorator' require 'active_record/fixtures' fixtures_dir = File.expand_path('../../db/default', __FILE__) @@ -92,6 +95,8 @@ RSpec.configure do |config| config.include Spree::CheckoutHelpers config.include Spree::Core::TestingSupport::ControllerRequests, :type => :controller config.include Devise::TestHelpers, :type => :controller + config.extend Spree::Api::TestingSupport::Setup, :type => :controller + config.include Spree::Api::TestingSupport::Helpers, :type => :controller config.include OpenFoodNetwork::FeatureToggleHelper config.include OpenFoodNetwork::EnterpriseGroupsHelper config.include OpenFoodNetwork::DistributionHelper