diff --git a/Gemfile b/Gemfile
index af4108c9eb..87566eebfa 100644
--- a/Gemfile
+++ b/Gemfile
@@ -35,6 +35,7 @@ gem 'geocoder'
gem 'gmaps4rails'
gem 'spinjs-rails'
gem 'rack-ssl', :require => 'rack/ssl'
+gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg'
gem 'foreigner'
gem 'immigrant'
diff --git a/Gemfile.lock b/Gemfile.lock
index 87a0acf967..733ef43572 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -6,6 +6,12 @@ GIT
actionpack (~> 3.0)
activemodel (~> 3.0)
+GIT
+ remote: git://github.com/jeremydurham/custom-err-msg.git
+ revision: 3a8ec9dddc7a5b0aab7c69a6060596de300c68f4
+ specs:
+ custom_error_message (1.1.1)
+
GIT
remote: git://github.com/openfoodfoundation/spree.git
revision: da651b40f5c6cdd32e00b060729eb9aefd4f615f
@@ -492,6 +498,7 @@ DEPENDENCIES
coffee-rails (~> 3.2.1)
comfortable_mexican_sofa
compass-rails
+ custom_error_message!
database_cleaner (= 0.7.1)
db2fog
debugger-linecache
diff --git a/app/assets/javascripts/admin/admin.js.coffee b/app/assets/javascripts/admin/admin.js.coffee
index ba07bb7933..76154e9e20 100644
--- a/app/assets/javascripts/admin/admin.js.coffee
+++ b/app/assets/javascripts/admin/admin.js.coffee
@@ -1,3 +1,3 @@
-window.Admin = angular.module("ofn.admin", ["ngResource","ofn.dropdown"]).config ($httpProvider) ->
+window.Admin = angular.module("ofn.admin", ["ngResource", "ngAnimate", "ofn.dropdown"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js
index 3cf4fe97e7..99c936c4d7 100644
--- a/app/assets/javascripts/admin/all.js
+++ b/app/assets/javascripts/admin/all.js
@@ -11,6 +11,7 @@
//= require shared/jquery-ui-timepicker-addon
//= require angular
//= require angular-resource
+//= require angular-animate
//= require admin/spree_core
//= require admin/spree_auth
//= require admin/spree_promo
diff --git a/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee b/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee
new file mode 100644
index 0000000000..c66e79c046
--- /dev/null
+++ b/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee
@@ -0,0 +1,10 @@
+Admin.controller "AdminEnterpriseRelationshipsCtrl", ($scope, EnterpriseRelationships, Enterprises) ->
+ $scope.EnterpriseRelationships = EnterpriseRelationships
+ $scope.Enterprises = Enterprises
+
+ $scope.create = ->
+ $scope.EnterpriseRelationships.create($scope.parent_id, $scope.child_id)
+
+ $scope.delete = (enterprise_relationship) ->
+ if confirm("Are you sure?")
+ $scope.EnterpriseRelationships.delete enterprise_relationship
diff --git a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee
new file mode 100644
index 0000000000..0469aa5637
--- /dev/null
+++ b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee
@@ -0,0 +1,18 @@
+Admin.factory 'EnterpriseRelationships', ($http, enterprise_relationships) ->
+ new class EnterpriseRelationships
+ create_errors: ""
+
+ constructor: ->
+ @enterprise_relationships = enterprise_relationships
+
+ create: (parent_id, child_id) ->
+ $http.post('/admin/enterprise_relationships', {enterprise_relationship: {parent_id: parent_id, child_id: child_id}}).success (data, status) =>
+ @enterprise_relationships.unshift(data)
+ @create_errors = ""
+
+ .error (response, status) =>
+ @create_errors = response.errors
+
+ delete: (er) ->
+ $http.delete('/admin/enterprise_relationships/' + er.id).success (data) =>
+ @enterprise_relationships.splice @enterprise_relationships.indexOf(er), 1
diff --git a/app/assets/javascripts/admin/services/enterprises.js.coffee b/app/assets/javascripts/admin/services/enterprises.js.coffee
new file mode 100644
index 0000000000..199b6294c3
--- /dev/null
+++ b/app/assets/javascripts/admin/services/enterprises.js.coffee
@@ -0,0 +1,4 @@
+Admin.factory 'Enterprises', (enterprises) ->
+ new class Enterprises
+ constructor: ->
+ @enterprises = enterprises
diff --git a/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee
index c2211bc033..da625e3d34 100644
--- a/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee
@@ -1,8 +1,11 @@
-Darkswarm.controller "LoginCtrl", ($scope, $http, $location, AuthenticationService) ->
+Darkswarm.controller "LoginCtrl", ($scope, $http, AuthenticationService, Redirections) ->
$scope.path = "/login"
$scope.submit = ->
$http.post("/user/spree_user/sign_in", {spree_user: $scope.spree_user}).success (data)->
- location.href = location.origin + location.pathname # Strips out hash fragments
+ if Redirections.after_login
+ location.href = location.origin + Redirections.after_login
+ else
+ location.href = location.origin + location.pathname # Strips out hash fragments
.error (data) ->
$scope.errors = data.message
diff --git a/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee
index 4ec316e65f..959423e3ca 100644
--- a/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee
@@ -1,4 +1,5 @@
Darkswarm.controller "ProductNodeCtrl", ($scope) ->
+
$scope.price = ->
if $scope.product.variants.length > 0
prices = (v.price for v in $scope.product.variants)
@@ -6,4 +7,6 @@ Darkswarm.controller "ProductNodeCtrl", ($scope) ->
else
$scope.product.price
+ $scope.producer = $scope.product.supplier
+
$scope.hasVariants = $scope.product.variants.length > 0
diff --git a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee
index 09df18ca38..323df2fc4e 100644
--- a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee
@@ -12,5 +12,3 @@ Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Product, OrderCycle) -
code = e.keyCode || e.which
if code == 13
e.preventDefault()
-
- $scope.productPrice = (product) ->
diff --git a/app/assets/javascripts/darkswarm/directives/modal.js.coffee b/app/assets/javascripts/darkswarm/directives/modal.js.coffee
index 73217b3812..6f62c7cb45 100644
--- a/app/assets/javascripts/darkswarm/directives/modal.js.coffee
+++ b/app/assets/javascripts/darkswarm/directives/modal.js.coffee
@@ -8,7 +8,10 @@ Darkswarm.directive "ofnModal", ($modal)->
link: (scope, elem, attrs, ctrl, transclude)->
scope.title = attrs.title
contents = null
- transclude scope, (clone)->
+
+ # We're using an isolate scope, which is a child of the original scope
+ # We have to compile the transclude against the original scope, not the isolate
+ transclude scope.$parent, (clone)->
contents = clone
elem.on "click", =>
diff --git a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee
index eada8bc44b..820d5bde9c 100644
--- a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location)->
+Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redirections)->
new class AuthenticationService
selectedPath: "/login"
diff --git a/app/assets/javascripts/darkswarm/services/product.js.coffee b/app/assets/javascripts/darkswarm/services/product.js.coffee
index ca31d5e9c0..e58b83bacc 100644
--- a/app/assets/javascripts/darkswarm/services/product.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/product.js.coffee
@@ -2,6 +2,9 @@ Darkswarm.factory 'Product', ($resource) ->
new class Product
constructor: ->
@update()
+
+ # TODO: don't need to scope this into object
+ # Already on object as far as controller scope is concerned
data:
products: null
loading: true
diff --git a/app/assets/javascripts/darkswarm/services/redirections.js.coffee b/app/assets/javascripts/darkswarm/services/redirections.js.coffee
new file mode 100644
index 0000000000..a479964e9e
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/services/redirections.js.coffee
@@ -0,0 +1,3 @@
+Darkswarm.factory "Redirections", ($location)->
+ new class Redirections
+ after_login: $location.search().after_login
diff --git a/app/assets/stylesheets/admin/enterprise_relationships.css.sass b/app/assets/stylesheets/admin/enterprise_relationships.css.sass
new file mode 100644
index 0000000000..cffc0b0623
--- /dev/null
+++ b/app/assets/stylesheets/admin/enterprise_relationships.css.sass
@@ -0,0 +1,20 @@
+// TODO: Provide -moz- and -o- directives
+@-webkit-keyframes alert-flash
+ 0%
+ background-color: #f9f1ae
+
+ 100%
+ background-color: #fff
+
+
+table#enterprise-relationships
+ th.actions, td.actions
+ width: 16%
+ .errors
+ color: #f00
+
+ tr.ng-enter
+ -webkit-animation-name: alert-flash
+ -webkit-animation-duration: 1200ms
+ -webkit-animation-iteration-count: 1
+ -webkit-animation-timing-function: ease-in-out
diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss
index 92092bd136..87983a6bc7 100644
--- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss
+++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss
@@ -19,9 +19,11 @@ table .blank-action {
}
+input.search {
+ margin-bottom: 1em;
+}
#new_enterprise_fee_set input.search {
float: right;
- margin-bottom: 1em;
}
.ng .ng-invalid.ng-dirty {
diff --git a/app/controllers/admin/enterprise_relationships_controller.rb b/app/controllers/admin/enterprise_relationships_controller.rb
new file mode 100644
index 0000000000..46ac45a3e5
--- /dev/null
+++ b/app/controllers/admin/enterprise_relationships_controller.rb
@@ -0,0 +1,24 @@
+module Admin
+ class EnterpriseRelationshipsController < ResourceController
+ def index
+ @enterprises = Enterprise.managed_by(spree_current_user).by_name
+ @enterprise_relationships = EnterpriseRelationship.by_name
+ end
+
+ def create
+ @enterprise_relationship = EnterpriseRelationship.new params[:enterprise_relationship]
+
+ if @enterprise_relationship.save
+ render partial: "admin/json/enterprise_relationship", locals: {enterprise_relationship: @enterprise_relationship}
+ else
+ render status: 400, json: {errors: @enterprise_relationship.errors.full_messages.join(', ')}
+ end
+ end
+
+ def destroy
+ @enterprise_relationship = EnterpriseRelationship.find params[:id]
+ @enterprise_relationship.destroy
+ render nothing: true
+ end
+ end
+end
diff --git a/app/controllers/spree/admin/overview_controller_decorator.rb b/app/controllers/spree/admin/overview_controller_decorator.rb
index 5c96901c1d..a6142d26c0 100644
--- a/app/controllers/spree/admin/overview_controller_decorator.rb
+++ b/app/controllers/spree/admin/overview_controller_decorator.rb
@@ -13,8 +13,7 @@ Spree::Admin::OverviewController.class_eval do
redirect_to '/unauthorized'
else
store_location
- url = respond_to?(:spree_login_path) ? spree_login_path : root_path
- redirect_to url
+ redirect_to root_path(anchor: "login?after_login=#{spree.admin_path}")
end
end
end
diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb
index ce20dfd58d..959e51204f 100644
--- a/app/controllers/spree/orders_controller_decorator.rb
+++ b/app/controllers/spree/orders_controller_decorator.rb
@@ -28,6 +28,7 @@ Spree::OrdersController.class_eval do
end
populator = Spree::OrderPopulator.new(current_order(true), current_currency)
if populator.populate(params.slice(:products, :variants, :quantity))
+
fire_event('spree.cart.add')
fire_event('spree.order.contents_changed')
respond_with(@order) do |format|
diff --git a/app/helpers/shared_helper.rb b/app/helpers/shared_helper.rb
index be1e0ba0f4..828277cb57 100644
--- a/app/helpers/shared_helper.rb
+++ b/app/helpers/shared_helper.rb
@@ -1,4 +1,9 @@
module SharedHelper
+
+ def inject_json(name, partial)
+ render "json/injection", name: name, partial: partial
+ end
+
def distributor_link_class(distributor)
cart = current_order(true)
@active_distributors ||= Enterprise.distributors_with_active_order_cycles
diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb
index 2cbda1511a..09fff8ba03 100644
--- a/app/models/enterprise_relationship.rb
+++ b/app/models/enterprise_relationship.rb
@@ -3,5 +3,10 @@ class EnterpriseRelationship < ActiveRecord::Base
belongs_to :child, class_name: 'Enterprise'
validates_presence_of :parent_id, :child_id
- validates_uniqueness_of :child_id, scope: :parent_id
+ validates_uniqueness_of :child_id, scope: :parent_id, message: "^That relationship is already established."
+
+ scope :with_enterprises,
+ joins('LEFT JOIN enterprises AS parent_enterprises ON parent_enterprises.id = enterprise_relationships.parent_id').
+ joins('LEFT JOIN enterprises AS child_enterprises ON child_enterprises.id = enterprise_relationships.child_id')
+ scope :by_name, with_enterprises.order('parent_enterprises.name, child_enterprises.name')
end
diff --git a/app/views/admin/enterprise_relationships/_data.html.haml b/app/views/admin/enterprise_relationships/_data.html.haml
new file mode 100644
index 0000000000..2361755707
--- /dev/null
+++ b/app/views/admin/enterprise_relationships/_data.html.haml
@@ -0,0 +1,3 @@
+:javascript
+ angular.module('ofn.admin').value('enterprise_relationships', #{render partial: "admin/json/enterprise_relationships", object: @enterprise_relationships});
+ angular.module('ofn.admin').value('enterprises', #{render partial: "admin/json/enterprises", object: @enterprises});
diff --git a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml
new file mode 100644
index 0000000000..3f93027ca1
--- /dev/null
+++ b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml
@@ -0,0 +1,5 @@
+%td {{ enterprise_relationship.parent_name }}
+%td permits
+%td {{ enterprise_relationship.child_name }}
+%td.actions
+ %a.delete-enterprise-relationship.icon-trash.no-text{'ng-click' => 'delete(enterprise_relationship)'}
diff --git a/app/views/admin/enterprise_relationships/_form.html.haml b/app/views/admin/enterprise_relationships/_form.html.haml
new file mode 100644
index 0000000000..5cc10b0fe1
--- /dev/null
+++ b/app/views/admin/enterprise_relationships/_form.html.haml
@@ -0,0 +1,9 @@
+%tr
+ %td
+ %select{name: "enterprise_relationship_parent_name", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.enterprises"}
+ %td permits
+ %td
+ %select{name: "enterprise_relationship_child_name", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.enterprises"}
+ %td.actions
+ %input{type: "button", value: "Create", "ng-click" => "create()"}
+ .errors {{ EnterpriseRelationships.create_errors }}
diff --git a/app/views/admin/enterprise_relationships/index.html.haml b/app/views/admin/enterprise_relationships/index.html.haml
new file mode 100644
index 0000000000..e0e289efcf
--- /dev/null
+++ b/app/views/admin/enterprise_relationships/index.html.haml
@@ -0,0 +1,15 @@
+- content_for :page_title do
+ Enterprise Relationships
+
+= render 'admin/shared/enterprises_sub_menu'
+
+%div{"ng-app" => "ofn.admin", "ng-controller" => "AdminEnterpriseRelationshipsCtrl"}
+ = render 'data'
+
+ %input.search{"ng-model" => "query", "placeholder" => "Search"}
+
+ %table#enterprise-relationships
+ %tbody
+ = render 'form'
+ %tr{"ng-repeat" => "enterprise_relationship in EnterpriseRelationships.enterprise_relationships | filter:query"}
+ = render 'enterprise_relationship'
diff --git a/app/views/admin/enterprises/index.html.erb b/app/views/admin/enterprises/index.html.erb
index d2917bcc45..1795b17950 100644
--- a/app/views/admin/enterprises/index.html.erb
+++ b/app/views/admin/enterprises/index.html.erb
@@ -8,6 +8,8 @@
<% end %>
+<%= render 'admin/shared/enterprises_sub_menu' %>
+
<%= form_for @enterprise_set, :url => main_app.bulk_update_admin_enterprises_path do |f| %>
diff --git a/app/views/admin/json/_enterprise_relationship.rabl b/app/views/admin/json/_enterprise_relationship.rabl
new file mode 100644
index 0000000000..9be152ec5c
--- /dev/null
+++ b/app/views/admin/json/_enterprise_relationship.rabl
@@ -0,0 +1,11 @@
+object @enterprise_relationship
+
+attributes :id, :parent_id, :child_id
+
+node :parent_name do |enterprise_relationship|
+ enterprise_relationship.parent.name
+end
+
+node :child_name do |enterprise_relationship|
+ enterprise_relationship.child.name
+end
diff --git a/app/views/admin/json/_enterprise_relationships.rabl b/app/views/admin/json/_enterprise_relationships.rabl
new file mode 100644
index 0000000000..aad55b9770
--- /dev/null
+++ b/app/views/admin/json/_enterprise_relationships.rabl
@@ -0,0 +1,2 @@
+collection @enterprise_relationships
+extends "admin/json/enterprise_relationship"
diff --git a/app/views/admin/json/_enterprises.rabl b/app/views/admin/json/_enterprises.rabl
new file mode 100644
index 0000000000..29e1dc7bf6
--- /dev/null
+++ b/app/views/admin/json/_enterprises.rabl
@@ -0,0 +1,3 @@
+collection @enterprises
+
+attributes :id, :name
diff --git a/app/views/admin/shared/_enterprises_sub_menu.html.haml b/app/views/admin/shared/_enterprises_sub_menu.html.haml
new file mode 100644
index 0000000000..4a793689b7
--- /dev/null
+++ b/app/views/admin/shared/_enterprises_sub_menu.html.haml
@@ -0,0 +1,4 @@
+= content_for :sub_menu do
+ %ul#sub_nav.inline-menu{"data-hook" => "admin_order_sub_tabs"}
+ = tab :enterprises, url: main_app.admin_enterprises_path
+ = tab :relationships, url: main_app.admin_enterprise_relationships_path, match_path: '/enterprise_relationships'
diff --git a/app/views/json/_injection.html.haml b/app/views/json/_injection.html.haml
new file mode 100644
index 0000000000..95b921406b
--- /dev/null
+++ b/app/views/json/_injection.html.haml
@@ -0,0 +1,2 @@
+:javascript
+ angular.module('Darkswarm').value("#{name.to_s}", #{render "json/#{partial.to_s}"})
diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml
index 5c2dcaee66..edeffafeed 100644
--- a/app/views/layouts/darkswarm.html.haml
+++ b/app/views/layouts/darkswarm.html.haml
@@ -15,16 +15,16 @@
= csrf_meta_tags
%body.off-canvas{"ng-app" => "Darkswarm"}
+ = inject_json "currentHub", "current_hub"
+ = inject_json "user", "current_user"
+
.off-canvas-wrap{offcanvas: true}
.inner-wrap
- = render partial: "shared/current_hub"
- = render partial: "shared/current_user"
= render partial: "shared/menu/menu"
+
= display_flash_messages
%ofn-flash
- -#= render "shared/sidebar"
-
%section{ role: "main" }
= yield
diff --git a/app/views/modals/_producer.html.haml b/app/views/modals/_producer.html.haml
index 691e9378d3..056f9e488f 100644
--- a/app/views/modals/_producer.html.haml
+++ b/app/views/modals/_producer.html.haml
@@ -1,10 +1,9 @@
%ofn-modal{title: "{{ producer.name }}"}
- #producer_modal{bindonce: true}
+ #producer_modal
.row
.small-12.columns
- %img{"bo-src" => "producer.promo_image"}
+ %img{"ng-src" => "producer.promo_image"}
%h3 {{ producer.name }}
-
.row
.small-6.columns
%p
diff --git a/app/views/shop/products/_modal.html.haml b/app/views/modals/_product.html.haml
similarity index 73%
rename from app/views/shop/products/_modal.html.haml
rename to app/views/modals/_product.html.haml
index 29a023704f..acca8f919b 100644
--- a/app/views/shop/products/_modal.html.haml
+++ b/app/views/modals/_product.html.haml
@@ -1,2 +1,4 @@
%ofn-modal{title: "{{product.name}}"}
+
+ {{ product | json }}
{{ product.description }}
diff --git a/app/views/shared/_current_hub.haml b/app/views/shared/_current_hub.haml
deleted file mode 100644
index 5a0ff7c564..0000000000
--- a/app/views/shared/_current_hub.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-:javascript
- angular.module('Darkswarm').value('currentHub', #{render "json/current_hub"})
diff --git a/app/views/shared/_current_user.haml b/app/views/shared/_current_user.haml
deleted file mode 100644
index 9745a71313..0000000000
--- a/app/views/shared/_current_user.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-:javascript
- angular.module('Darkswarm').value('user', #{render "json/current_user"})
diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml
index 5e777a5dbb..e41c6ef440 100644
--- a/app/views/shop/products/_summary.html.haml
+++ b/app/views/shop/products/_summary.html.haml
@@ -6,11 +6,10 @@
%img{"bo-src" => "product.primary_taxon.icon",
"ng-click" => "ordering.order = 'primary_taxon.name'",
name: "{{product.primary_taxon.name}}"}
- {{ product.name}}
- -#= render partial: "shop/products/modal"
+ = render partial: "modals/product"
.small-5.columns.summary-header
- {{ product.supplier.name }}
+ = render partial: "modals/producer"
.small-2.columns.summary-price.text-right.price
%span{"ng-if" => "hasVariants"}
diff --git a/config/routes.rb b/config/routes.rb
index 310d1d9e4d..973cb288cc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -41,6 +41,8 @@ Openfoodnetwork::Application.routes.draw do
post :bulk_update, :on => :collection, :as => :bulk_update
end
+ resources :enterprise_relationships
+
resources :enterprise_fees do
post :bulk_update, :on => :collection, :as => :bulk_update
end
diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb
index 53c7be68f0..e6c9b91894 100644
--- a/spec/controllers/spree/orders_controller_spec.rb
+++ b/spec/controllers/spree/orders_controller_spec.rb
@@ -15,6 +15,7 @@ describe Spree::OrdersController do
controller.stub(:current_order_cycle).and_return(order_cycle)
controller.stub(:current_order).and_return order
order.stub_chain(:line_items, :empty?).and_return true
+ session[:access_token] = order.token
spree_get :edit
response.should redirect_to shop_path
end
diff --git a/spec/factories.rb b/spec/factories.rb
index 13c0bd0e8e..6da7824d47 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -96,6 +96,9 @@ FactoryGirl.define do
is_distributor true
end
+ factory :enterprise_relationship do
+ end
+
factory :enterprise_group, :class => EnterpriseGroup do
name 'Enterprise group'
description 'this is a group'
@@ -187,6 +190,10 @@ FactoryGirl.modify do
distributors { [Enterprise.is_distributor.first || FactoryGirl.create(:distributor_enterprise)] }
end
+ factory :option_type do
+ # Prevent inconsistent ordering in specs when all option types have the same (0) position
+ sequence(:position)
+ end
end
diff --git a/spec/features/admin/enterprise_relationships_spec.rb b/spec/features/admin/enterprise_relationships_spec.rb
new file mode 100644
index 0000000000..ea95f86ba4
--- /dev/null
+++ b/spec/features/admin/enterprise_relationships_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+feature %q{
+ As an Administrator
+ I want to manage relationships between enterprises
+}, js: true do
+ include AuthenticationWorkflow
+ include WebHelper
+
+ before { login_to_admin_section }
+
+
+ scenario "listing relationships" do
+ # Given some enterprises with relationships
+ e1, e2, e3, e4 = create(:enterprise), create(:enterprise), create(:enterprise), create(:enterprise)
+ create(:enterprise_relationship, parent: e1, child: e2)
+ create(:enterprise_relationship, parent: e3, child: e4)
+
+ # When I go to the relationships page
+ click_link 'Enterprises'
+ click_link 'Relationships'
+
+ # Then I should see the relationships
+ within('table#enterprise-relationships') do
+ page.should have_table_row [e1.name, 'permits', e2.name, '']
+ page.should have_table_row [e3.name, 'permits', e4.name, '']
+ end
+ end
+
+
+ scenario "creating a relationship" do
+ e1 = create(:enterprise, name: 'One')
+ e2 = create(:enterprise, name: 'Two')
+
+ visit admin_enterprise_relationships_path
+ select 'One', from: 'enterprise_relationship_parent_name'
+ select 'Two', from: 'enterprise_relationship_child_name'
+ click_button 'Create'
+
+ page.should have_table_row [e1.name, 'permits', e2.name, '']
+ EnterpriseRelationship.where(parent_id: e1, child_id: e2).should be_present
+ end
+
+
+ scenario "attempting to create a relationship with invalid data" do
+ e1 = create(:enterprise, name: 'One')
+ e2 = create(:enterprise, name: 'Two')
+ create(:enterprise_relationship, parent: e1, child: e2)
+
+ expect do
+ # When I attempt to create a duplicate relationship
+ visit admin_enterprise_relationships_path
+ select 'One', from: 'enterprise_relationship_parent_name'
+ select 'Two', from: 'enterprise_relationship_child_name'
+ click_button 'Create'
+
+ # Then I should see an error message
+ page.should have_content "That relationship is already established."
+ end.to change(EnterpriseRelationship, :count).by(0)
+ end
+
+
+ scenario "deleting a relationship" do
+ e1 = create(:enterprise, name: 'One')
+ e2 = create(:enterprise, name: 'Two')
+ er = create(:enterprise_relationship, parent: e1, child: e2)
+
+ visit admin_enterprise_relationships_path
+ page.should have_table_row [e1.name, 'permits', e2.name, '']
+
+ first("a.delete-enterprise-relationship").click
+
+ page.should_not have_table_row [e1.name, 'permits', e2.name, '']
+ EnterpriseRelationship.where(id: er.id).should be_empty
+ end
+end
diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb
index 77b60290d1..76e4a20877 100644
--- a/spec/features/consumer/authentication_spec.rb
+++ b/spec/features/consumer/authentication_spec.rb
@@ -5,7 +5,27 @@ feature "Authentication", js: true do
describe "login" do
let(:user) { create(:user, password: "password", password_confirmation: "password") }
- describe "newskool" do
+ describe "With redirects" do
+ scenario "logging in with a redirect set" do
+ visit groups_path(anchor: "login?after_login=#{producers_path}")
+ fill_in "Email", with: user.email
+ fill_in "Password", with: user.password
+ click_login_button
+ page.should have_content "Select a producer from the list below"
+ current_path.should == producers_path
+ end
+
+ scenario "logging into admin redirects home, then back to admin" do
+ visit spree.admin_path
+ fill_in "Email", with: user.email
+ fill_in "Password", with: user.password
+ click_login_button
+ page.should have_content "Dashboard"
+ current_path.should == spree.admin_path
+ end
+ end
+
+ describe "Loggin in from the home page" do
before do
visit root_path
end
diff --git a/spec/javascripts/unit/darkswarm/controllers/hub_node_controller_spec.js.coffee b/spec/javascripts/unit/darkswarm/controllers/hub_node_controller_spec.js.coffee
index a2d14f5811..cb77cb1703 100644
--- a/spec/javascripts/unit/darkswarm/controllers/hub_node_controller_spec.js.coffee
+++ b/spec/javascripts/unit/darkswarm/controllers/hub_node_controller_spec.js.coffee
@@ -19,14 +19,3 @@ describe "HubNodeCtrl", ->
expect(scope.current()).toEqual false
scope.hub = {id: 99}
expect(scope.current()).toEqual true
-
- it "knows whether selecting this hub will empty the cart", ->
- CurrentHub.id = undefined
- expect(scope.emptiesCart()).toEqual false
-
- CurrentHub.id = 99
- scope.hub.id = 99
- expect(scope.emptiesCart()).toEqual false
-
- scope.hub.id = 1
- expect(scope.emptiesCart()).toEqual true
diff --git a/spec/javascripts/unit/darkswarm/controllers/products/product_node_controller_spec.js.coffee b/spec/javascripts/unit/darkswarm/controllers/products/product_node_controller_spec.js.coffee
new file mode 100644
index 0000000000..4b9e404b9e
--- /dev/null
+++ b/spec/javascripts/unit/darkswarm/controllers/products/product_node_controller_spec.js.coffee
@@ -0,0 +1,24 @@
+describe "ProductNodeCtrl", ->
+ ctrl = null
+ scope = null
+ product =
+ id: 99
+ price: 10.00
+ variants: []
+
+ beforeEach ->
+ module('Darkswarm')
+ inject ($controller) ->
+ scope =
+ product: product
+ ctrl = $controller 'ProductNodeCtrl', {$scope: scope}
+
+ describe "determining the price to display for a product", ->
+ it "displays the product price when the product does not have variants", ->
+ expect(scope.price()).toEqual 10.00
+
+ it "displays the minimum variant price when the product has variants", ->
+ scope.product =
+ price: 11
+ variants: [{price: 22}, {price: 33}]
+ expect(scope.price()).toEqual 22
diff --git a/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee b/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee
index 269c8385b8..685d495039 100644
--- a/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee
+++ b/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee
@@ -1,47 +1,21 @@
-describe 'All controllers', ->
- describe 'ProductsCtrl', ->
- ctrl = null
- scope = null
- event = null
- Product = null
+describe 'ProductsCtrl', ->
+ ctrl = null
+ scope = null
+ event = null
+ Product = null
- beforeEach ->
- module('Darkswarm')
- Product =
- all: ->
- update: ->
- data: "testy mctest"
- OrderCycle =
- order_cycle: {}
-
- inject ($controller) ->
- scope = {}
- ctrl = $controller 'ProductsCtrl', {$scope: scope, Product: Product, OrderCycle: OrderCycle}
-
- it 'fetches products from Product', ->
- expect(scope.data).toEqual 'testy mctest'
-
- describe "determining the price to display for a product", ->
- it "displays the product price when the product does not have variants", ->
- product = {variants: [], price: 12.34}
- expect(scope.productPrice(product)).toEqual 12.34
-
- it "displays the minimum variant price when the product has variants", ->
- product =
- price: 11
- variants: [{price: 22}, {price: 33}]
- expect(scope.productPrice(product)).toEqual 22
-
- describe 'OrderCycleCtrl', ->
- ctrl = null
- scope = null
- event = null
- product_ctrl = null
- OrderCycle = null
-
- beforeEach ->
- module 'Darkswarm'
+ beforeEach ->
+ module('Darkswarm')
+ Product =
+ all: ->
+ update: ->
+ data: "testy mctest"
+ OrderCycle =
+ order_cycle: {}
+
+ inject ($controller) ->
scope = {}
- inject ($controller) ->
- scope = {}
- ctrl = $controller 'OrderCycleCtrl', {$scope: scope}
+ ctrl = $controller 'ProductsCtrl', {$scope: scope, Product: Product, OrderCycle: OrderCycle}
+
+ it 'fetches products from Product', ->
+ expect(scope.data).toEqual 'testy mctest'
diff --git a/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee
index 0462e92952..fedcf23629 100644
--- a/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee
+++ b/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee
@@ -26,8 +26,10 @@ describe 'Order service', ->
}
angular.module('Darkswarm').value('order', orderData)
module 'Darkswarm'
+
inject ($injector, _$httpBackend_)->
$httpBackend = _$httpBackend_
+ $httpBackend.expectGET("/shop/products").respond 200, []
Order = $injector.get("Order")
Navigation = $injector.get("Navigation")
flash = $injector.get("flash")
diff --git a/spec/javascripts/unit/darkswarm/product_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee
similarity index 86%
rename from spec/javascripts/unit/darkswarm/product_spec.js.coffee
rename to spec/javascripts/unit/darkswarm/services/product_spec.js.coffee
index 7108d819af..260074a981 100644
--- a/spec/javascripts/unit/darkswarm/product_spec.js.coffee
+++ b/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee
@@ -10,5 +10,5 @@ describe 'Product service', ->
it "Fetches products from the backend on init", ->
$httpBackend.expectGET("/shop/products").respond([{test : "cats"}])
- products = Product.all()
$httpBackend.flush()
+ expect(Product.data.products[0].test).toEqual "cats"
diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb
new file mode 100644
index 0000000000..7cca07472e
--- /dev/null
+++ b/spec/models/enterprise_relationship_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe EnterpriseRelationship do
+ describe "scopes" do
+ it "sorts by parent, child enterprise name" do
+ e1 = create(:enterprise, name: 'A')
+ e2 = create(:enterprise, name: 'B')
+ e3 = create(:enterprise, name: 'C')
+ er1 = create(:enterprise_relationship, parent: e1, child: e3)
+ er2 = create(:enterprise_relationship, parent: e2, child: e1)
+ er3 = create(:enterprise_relationship, parent: e1, child: e2)
+
+ EnterpriseRelationship.by_name.should == [er3, er1, er2]
+ end
+ end
+end
diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb
index 06c4c0850e..f375b8890a 100644
--- a/spec/models/enterprise_spec.rb
+++ b/spec/models/enterprise_spec.rb
@@ -31,10 +31,10 @@ describe Enterprise do
let(:e) { create(:distributor_enterprise) }
let(:p) { create(:supplier_enterprise) }
let(:c) { create(:distributor_enterprise) }
- before do
- EnterpriseRelationship.create! parent_id: p.id, child_id: e.id
- EnterpriseRelationship.create! parent_id: e.id, child_id: c.id
- end
+
+ let!(:er1) { create(:enterprise_relationship, parent_id: p.id, child_id: e.id) }
+ let!(:er2) { create(:enterprise_relationship, parent_id: e.id, child_id: c.id) }
+
it "finds relatives" do
e.relatives.sort.should == [p, c].sort
end
diff --git a/spec/support/matchers/table_matchers.rb b/spec/support/matchers/table_matchers.rb
new file mode 100644
index 0000000000..146002b751
--- /dev/null
+++ b/spec/support/matchers/table_matchers.rb
@@ -0,0 +1,43 @@
+RSpec::Matchers.define :have_table_row do |row|
+
+ match_for_should do |node|
+ @row = row
+
+ false_on_timeout_error do
+ wait_until { rows_under(node).include? row }
+ end
+ end
+
+ match_for_should_not do |node|
+ @row = row
+
+ false_on_timeout_error do
+ # Without this sleep, we trigger capybara's wait when looking up the table, for the full
+ # period of default_wait_time.
+ sleep 0.1
+ wait_until { !rows_under(node).include? row }
+ end
+ end
+
+ failure_message_for_should do |text|
+ "expected to find table row #{@row}"
+ end
+
+ failure_message_for_should_not do |text|
+ "expected not to find table row #{@row}"
+ end
+
+
+ def rows_under(node)
+ node.all('tr').map { |tr| tr.all('th, td').map(&:text) }
+ end
+
+ def false_on_timeout_error
+ yield
+ rescue TimeoutError
+ false
+ else
+ true
+ end
+
+end
diff --git a/spec/views/admin/json/enterprise_relationships_rabl_spec.rb b/spec/views/admin/json/enterprise_relationships_rabl_spec.rb
new file mode 100644
index 0000000000..9b72a0d45b
--- /dev/null
+++ b/spec/views/admin/json/enterprise_relationships_rabl_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe "admin/json/_enterprise_relationships.json.rabl" do
+ let(:parent) { create(:enterprise) }
+ let(:child) { create(:enterprise) }
+ let(:enterprise_relationship) { create(:enterprise_relationship, parent: parent, child: child) }
+ let(:render) { Rabl.render([enterprise_relationship], 'admin/json/enterprise_relationships', view_path: 'app/views', scope: RablHelper::FakeContext.instance) }
+
+ it "renders a list of enterprise relationships" do
+ render.should have_json_type(Array).at_path ''
+ render.should have_json_type(Object).at_path '0'
+ end
+
+ it "renders enterprise ids" do
+ render.should be_json_eql(parent.id).at_path '0/parent_id'
+ render.should be_json_eql(child.id).at_path '0/child_id'
+ end
+
+ it "renders enterprise names" do
+ render.should be_json_eql(parent.name.to_json).at_path '0/parent_name'
+ render.should be_json_eql(child.name.to_json).at_path '0/child_name'
+ end
+end