diff --git a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee index 98053daebf..b44d63eadf 100644 --- a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.controller "HubsCtrl", ($scope, Hubs, Search, $document, $rootScope, HashNavigation, FilterSelectorsService) -> +Darkswarm.controller "HubsCtrl", ($scope, Hubs, Search, $document, $rootScope, HashNavigation, FilterSelectorsService, MapModal) -> $scope.Hubs = Hubs $scope.hubs = Hubs.visible $scope.totalActive = FilterSelectorsService.totalActive @@ -6,6 +6,8 @@ Darkswarm.controller "HubsCtrl", ($scope, Hubs, Search, $document, $rootScope, H $scope.filterText = FilterSelectorsService.filterText $scope.FilterSelectorsService = FilterSelectorsService $scope.query = Search.search() + $scope.show_profiles = false + $scope.openModal = MapModal.open $scope.$watch "query", (query)-> Search.search query diff --git a/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee index d88af1e53d..b16dcdfa77 100644 --- a/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.controller "ProducersCtrl", ($scope, Producers, $filter, FilterSelectorsService, Search) -> +Darkswarm.controller "ProducersCtrl", ($scope, Producers, $filter, FilterSelectorsService, Search, MapModal) -> $scope.Producers = Producers $scope.totalActive = FilterSelectorsService.totalActive $scope.clearAll = FilterSelectorsService.clearAll @@ -7,6 +7,8 @@ Darkswarm.controller "ProducersCtrl", ($scope, Producers, $filter, FilterSelecto $scope.filtersActive = false $scope.activeTaxons = [] $scope.query = Search.search() + $scope.show_profiles = false + $scope.openModal = MapModal.open $scope.$watch "query", (query)-> Search.search query diff --git a/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee new file mode 100644 index 0000000000..1fecfbf804 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee @@ -0,0 +1,13 @@ +Darkswarm.directive "ofnRegistrationLimitModal", (Navigation, $modal, Loading) -> + restrict: 'A' + link: (scope, elem, attr)-> + scope.modalInstance = $modal.open + templateUrl: 'registration/limit_reached.html' + windowClass: "login-modal large" + backdrop: 'static' + + scope.modalInstance.result.then scope.close, scope.close + + scope.close = -> + Loading.message = "Taking you back to the home page" + Navigation.go "/" diff --git a/app/assets/javascripts/darkswarm/filters/show_profiles.js.coffee b/app/assets/javascripts/darkswarm/filters/show_profiles.js.coffee new file mode 100644 index 0000000000..e3fce7d2f6 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/show_profiles.js.coffee @@ -0,0 +1,7 @@ +Darkswarm.filter 'showProfiles', ()-> + (enterprises, show_profiles) -> + enterprises ||= [] + show_profiles ?= true + + enterprises.filter (enterprise)=> + show_profiles or enterprise.has_shopfront diff --git a/app/assets/javascripts/darkswarm/services/hubs.js.coffee b/app/assets/javascripts/darkswarm/services/hubs.js.coffee index de9900866f..ba3d9f3c24 100644 --- a/app/assets/javascripts/darkswarm/services/hubs.js.coffee +++ b/app/assets/javascripts/darkswarm/services/hubs.js.coffee @@ -2,7 +2,7 @@ Darkswarm.factory 'Hubs', ($filter, Enterprises, visibleFilter) -> new class Hubs constructor: -> @hubs = @order Enterprises.enterprises.filter (hub)-> - hub.is_distributor && hub.has_shopfront + hub.has_hub_listing @visible = visibleFilter @hubs order: (hubs)-> diff --git a/app/assets/javascripts/templates/partials/enterprise_header.html.haml b/app/assets/javascripts/templates/partials/enterprise_header.html.haml index 96a4fb207e..8612ba968e 100644 --- a/app/assets/javascripts/templates/partials/enterprise_header.html.haml +++ b/app/assets/javascripts/templates/partials/enterprise_header.html.haml @@ -1,12 +1,13 @@ -.highlight - .highlight-top - %p.right - {{ [enterprise.address.city, enterprise.address.state_name] | printArray}} - %h3{"ng-if" => "enterprise.is_distributor"} - %a{"bo-href" => "enterprise.path", "ofn-empties-cart" => "enterprise", bindonce: true} - %i.ofn-i_040-hub +.highlight{"ng-class" => "{'has_shopfront' : enterprise.has_shopfront}"} + .highlight-top.row + .small-12.medium-7.large-8.columns + %h3{"ng-if" => "enterprise.has_shopfront"} + %a{"bo-href" => "enterprise.path", "ofn-empties-cart" => "enterprise", bindonce: true} + %i{"ng-class" => "enterprise.icon_font"} + %span {{ enterprise.name }} + %h3{"ng-if" => "!enterprise.has_shopfront", "ng-class" => "{'is_producer' : enterprise.is_primary_producer}"} + %i{"ng-class" => "enterprise.icon_font"} %span {{ enterprise.name }} - %h3{"ng-if" => "!enterprise.is_distributor"} - %i.ofn-i_036-producers - %span {{ enterprise.name }} - %img.hero-img{"ng-src" => "{{enterprise.promo_image}}"} + .small-12.medium-5.large-4.columns.text-right.small-only-text-left + %p {{ [enterprise.address.city, enterprise.address.state_name] | printArray}} + %img.hero-img{"ng-src" => "{{enterprise.promo_image}}"} \ No newline at end of file diff --git a/app/assets/javascripts/templates/registration/about.html.haml b/app/assets/javascripts/templates/registration/about.html.haml index 57f7c482e6..d014c96d90 100644 --- a/app/assets/javascripts/templates/registration/about.html.haml +++ b/app/assets/javascripts/templates/registration/about.html.haml @@ -28,8 +28,8 @@ .row .small-12.columns %label{ for: 'enterprise_long_desc' } Long Description: - %textarea.chunky.small-12.columns{ id: 'enterprise_long_desc', placeholder: "We recommend keeping your description to under 600 characters or 150 words. Why? Cus people are lazy, and don't like to read too much text online. ;)", ng: { model: 'enterprise.long_description' } } - %small {{ enterprise.long_description.length }} characters used + %textarea.chunky.small-12.columns{ id: 'enterprise_long_desc', rows: 6, placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words.", ng: { model: 'enterprise.long_description' } } + %small {{ enterprise.long_description.length }} characters / up to 600 recommended .small-12.large-4.columns .row .small-12.columns diff --git a/app/assets/javascripts/templates/registration/limit_reached.html.haml b/app/assets/javascripts/templates/registration/limit_reached.html.haml new file mode 100644 index 0000000000..e2131c1727 --- /dev/null +++ b/app/assets/javascripts/templates/registration/limit_reached.html.haml @@ -0,0 +1,15 @@ +%div + .header.center + %h2 Oh no! + %h4 You have reached the limit! + .row + .small-12.medium-3.large-2.columns.text-right.hide-for-small-only + %img{:src => "/assets/potatoes.png"} + .small-12.medium-9.large-10.columns + %p + You have reached the limit for the number of enterprises you are allowed to own on the + %strong Open Food Network. + .row + .small-12.columns + %hr + %input.button.primary{ type: "button", value: "Return to the homepage", ng: { click: "close()" } } diff --git a/app/assets/stylesheets/darkswarm/branding.css.sass b/app/assets/stylesheets/darkswarm/branding.css.sass index 011ef2ff68..0fa559240f 100644 --- a/app/assets/stylesheets/darkswarm/branding.css.sass +++ b/app/assets/stylesheets/darkswarm/branding.css.sass @@ -17,7 +17,9 @@ $clr-blue-bright: #14b6cc $disabled-light: #e5e5e5 $disabled-bright: #ccc +$disabled-med: #b3b3b3 $disabled-dark: #999 +$disabled-v-dark: #808080 $med-grey: #666 $dark-grey: #333 - +$black: #000 diff --git a/app/assets/stylesheets/darkswarm/hub_node.css.sass b/app/assets/stylesheets/darkswarm/hub_node.css.sass index eb539cc0b5..d02f9b5299 100644 --- a/app/assets/stylesheets/darkswarm/hub_node.css.sass +++ b/app/assets/stylesheets/darkswarm/hub_node.css.sass @@ -87,6 +87,8 @@ &.inactive &.closed, &.open &, & * + color: $disabled-med + a, a * color: $disabled-dark &.closed .active_table_row, .active_table_row:first-child, .active_table_row:last-child @@ -126,3 +128,13 @@ .active_table_row:first-child .skinny-head background-color: $disabled-light + //Is Profile - profile node + &.inactive.is_profile + &.closed, &.open + .active_table_row + &:hover, &:active, &:focus + border-color: transparent + cursor: auto + @media all and (max-width: 640px) + border-color: transparent + diff --git a/app/assets/stylesheets/darkswarm/images.css.sass b/app/assets/stylesheets/darkswarm/images.css.sass index ce205f0dae..f94240b4af 100644 --- a/app/assets/stylesheets/darkswarm/images.css.sass +++ b/app/assets/stylesheets/darkswarm/images.css.sass @@ -11,12 +11,16 @@ @include box-shadow(0 1px 2px 1px rgba(0,0,0,0.25)) .hero-img - border-bottom: 1px solid $disabled-bright + outline: 1px solid $disabled-bright + border-color: transparent + @include box-shadow(none) width: 100% - min-height: 56px + min-height: 80px height: inherit max-height: 260px overflow: hidden + @media all and (max-width: 640px) + min-height: 68px .hero-img-small background-color: #333 diff --git a/app/assets/stylesheets/darkswarm/mixins.sass b/app/assets/stylesheets/darkswarm/mixins.sass index 6eef888a17..6f4cb65440 100644 --- a/app/assets/stylesheets/darkswarm/mixins.sass +++ b/app/assets/stylesheets/darkswarm/mixins.sass @@ -16,6 +16,20 @@ -webkit-box-shadow: $box-shadow box-shadow: $box-shadow +@mixin elipse-shadow($elipse-shadow) + content: "" + position: absolute + z-index: -1 + -webkit-box-shadow: $elipse-shadow + box-shadow: $elipse-shadow + bottom: -12% + left: 10% + right: 10% + width: 80% + height: 10% + -moz-border-radius: 100% + border-radius: 100% + @mixin border-radius($border-radius) -webkit-border-radius: $border-radius border-radius: $border-radius diff --git a/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass b/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass index 9f08b38790..305566ad58 100644 --- a/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass +++ b/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass @@ -25,23 +25,39 @@ position: relative .highlight-top - padding: 0.75rem 0.9375rem - width: 100% - overflow: hidden + padding-top: 0.75rem + padding-bottom: 0.75rem background-color: rgba(255,255,255,0.65) position: absolute bottom: 0 + width: 100% + border: 0 + outline: 0 + @media only screen and (max-width: 640px) + padding-top: 0.5rem + padding-bottom: 0.35rem + h3, p margin-top: 0 margin-bottom: 0 padding-bottom: 0 line-height: 1 + + h3 > i + color: $clr-brick + p - line-height: 2 + line-height: 2.4 + @media all and (max-width: 640px) + line-height: 1.4 h3 a:hover span border-bottom: 1px solid $clr-brick-bright + .is_producer + &, & * + color: $clr-turquoise + // ABOUT Enterprise diff --git a/app/assets/stylesheets/darkswarm/modal-login.css.sass b/app/assets/stylesheets/darkswarm/modal-login.css.sass index 38fe5dee3a..61108145bb 100644 --- a/app/assets/stylesheets/darkswarm/modal-login.css.sass +++ b/app/assets/stylesheets/darkswarm/modal-login.css.sass @@ -1,6 +1,13 @@ // Styling for login modal to style tabs +.reveal-modal.login-modal + border-bottom-color: #efefef + .login-modal background: #efefef .tabs-content - background: #fff \ No newline at end of file + background: #fff + padding-top: 10px + + + \ No newline at end of file diff --git a/app/assets/stylesheets/darkswarm/modals.css.sass b/app/assets/stylesheets/darkswarm/modals.css.sass index e6303a4cd7..578a3ceccb 100644 --- a/app/assets/stylesheets/darkswarm/modals.css.sass +++ b/app/assets/stylesheets/darkswarm/modals.css.sass @@ -4,8 +4,14 @@ dialog, .reveal-modal border: none outline: none - padding: 1rem + padding: 30px 20px 0 20px + border-bottom: 30px solid white overflow-y: scroll + overflow-x: hidden + // Not working yet - want a nice gradient shadow when there is overflow - needs JS too + // &:after + // @include elipse-shadow(0 0 40px rgba(0, 0, 0, 0.8)) + // Reveal.js break point: // @media only screen and (max-width: 40.063em) @@ -25,14 +31,18 @@ dialog, .reveal-modal max-height: 80% .reveal-modal-bg - background-color: rgba(0,0,0,0.65) + background-color: rgba(0,0,0,0.85) dialog .close-reveal-modal, .reveal-modal .close-reveal-modal - right: 0.4rem - background-color: rgba(235,235,235,0.85) + right: 0.25rem + top: 0.25rem + background-color: rgba(205,205,205,0.65) text-shadow: none - padding: 0.3rem + font-size: 2rem + padding: 0.45rem + color: #666 + z-index: 9999999 @include border-radius(999999rem) &:hover, &:active, &:focus - background-color: rgba(235,235,235,1) + background-color: rgba(205,205,205,1) color: #333 diff --git a/app/assets/stylesheets/darkswarm/producer_node.css.sass b/app/assets/stylesheets/darkswarm/producer_node.css.sass index 08c9fd87ef..22541055f4 100644 --- a/app/assets/stylesheets/darkswarm/producer_node.css.sass +++ b/app/assets/stylesheets/darkswarm/producer_node.css.sass @@ -32,6 +32,11 @@ span text-decoration: underline + &.has_shopfront, &.has_shopfront i.ofn-i_059-producer, &.has_shopfront i.ofn-i_060-producer-reversed + color: $clr-brick + &:hover, &:active, &:focus + color: $clr-brick-bright + a.cta-hub &:hover, &:focus, &:active &.secondary @@ -51,9 +56,11 @@ .fat-taxons background-color: $clr-turquoise-light + .producer-name + color: $clr-turquoise + //Open row &.open - .active_table_row border-left: 1px solid $clr-turquoise-bright border-right: 1px solid $clr-turquoise-bright diff --git a/app/controllers/registration_controller.rb b/app/controllers/registration_controller.rb index 47a1537b57..4fae68bb79 100644 --- a/app/controllers/registration_controller.rb +++ b/app/controllers/registration_controller.rb @@ -20,6 +20,8 @@ class RegistrationController < BaseController def check_user if spree_current_user.nil? redirect_to registration_auth_path(anchor: "signup?after_login=#{request.env['PATH_INFO']}") + elsif !spree_current_user.can_own_more_enterprises? + render :limit_reached end end end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index ec397c10dc..dfb7945878 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -51,9 +51,9 @@ class Enterprise < ActiveRecord::Base validates :address, presence: true, associated: true validates :email, presence: true validates_presence_of :owner - validate :enforce_ownership_limit, if: lambda { owner_id_changed? } + validate :enforce_ownership_limit, if: lambda { owner_id_changed? && !owner_id.nil? } - before_validation :ensure_owner_is_manager, if: lambda { owner_id_changed? } + before_validation :ensure_owner_is_manager, if: lambda { owner_id_changed? && !owner_id.nil? } before_validation :set_unused_address_fields after_validation :geocode_address @@ -211,6 +211,39 @@ class Enterprise < ActiveRecord::Base Spree::Variant.joins(:product => :product_distributions).where('product_distributions.distributor_id=?', self.id) end + # Replaces currententerprse type field. + def sells + # Type: full - single - profile becomes Sells: all - own - none + # Remove this return later. + return "none" if !is_distributor || type == "profile" + return "own" if type == "single" || suppliers == [self] + "all" + end + + # Simplify enterprise categories for frontend logic and icons, and maybe other things. + def enterprise_category + # Make this crazy logic human readable so we can argue about it sanely. + # This can be simplified later, it's like this for readablitlty during changes. + category = is_primary_producer ? "producer_" : "non_producer_" + category << "sell_" + sells + + # Map backend cases to front end cases. + case category + when "producer_sell_all" + "producer_hub" # Producer hub who sells own and others produce and supplies other hubs. + when "producer_sell_own" + "producer_shop" # Producer with shopfront and supplies other hubs. + when "producer_sell_none" + "producer" # Producer only supplies through others. + when "non_producer_sell_all" + "hub" # Hub selling others products in order cycles. + when "non_producer_sell_own" + "hub" # Wholesaler selling through own shopfront? + when "non_producer_sell_none" + "hub_profile" # Hub selling outside the system. + end + end + # Return all taxons for all distributed products def distributed_taxons Spree::Taxon. @@ -227,7 +260,6 @@ class Enterprise < ActiveRecord::Base select('DISTINCT spree_taxons.*') end - private def send_creation_email diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 08fc4407ce..5fdb4c2342 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -5,6 +5,7 @@ class AbilityDecorator add_base_abilities user if is_new_user? user add_enterprise_management_abilities user if can_manage_enterprises? user add_product_management_abilities user if can_manage_products? user + add_order_management_abilities user if can_manage_orders? user add_relationship_management_abilities user if can_manage_relationships? user end @@ -17,11 +18,13 @@ class AbilityDecorator user.enterprises.present? end - def can_manage_products?(user) - ( user.enterprises.map(&:type) & %w(single full) ).any? + can_manage_enterprises? user end + def can_manage_orders?(user) + ( user.enterprises.map(&:type) & %w(single full) ).any? + end def can_manage_relationships?(user) can_manage_enterprises? user @@ -46,7 +49,6 @@ class AbilityDecorator end end - def add_product_management_abilities(user) # Enterprise User can only access products that they are a supplier for can [:create], Spree::Product @@ -64,7 +66,9 @@ class AbilityDecorator can [:admin, :index, :read, :search], Spree::Taxon can [:admin, :index, :read, :create, :edit], Spree::Classification + end + def add_order_management_abilities(user) # Enterprise User can only access orders that they are a distributor for can [:index, :create], Spree::Order can [:read, :update, :fire, :resend], Spree::Order do |order| diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index c76cffd7d1..6841337024 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -4,7 +4,7 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer end private - + def cached_serializer_hash Api::CachedEnterpriseSerializer.new(object, @options).serializable_hash end @@ -18,7 +18,7 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer attributes :orders_close_at, :active #TODO: Remove these later - attributes :icon, :has_shopfront, :can_aggregate + attributes :icon, :icon_font, :producer_icon_font, :has_shopfront, :has_hub_listing, :enterprise_category def orders_close_at OrderCycle.first_closing_for(object).andand.orders_close_at @@ -28,41 +28,61 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer @options[:active_distributors].andand.include? object end - # TODO: Move this back to uncached section when relavant properties are defined on the Enterprise model - def icon - # TODO: Replace with object.has_shopfront when this property exists - if has_shopfront - if can_aggregate - "/assets/map_005-hub.svg" - else - if object.is_distributor - "/assets/map_003-producer-shop.svg" - else - "/assets/map_001-producer-only.svg" - end - end - else - if can_aggregate - "/assets/map_006-hub-profile.svg" - else - if object.is_distributor - "/assets/map_004-producer-shop-profile.svg" - else - "/assets/map_002-producer-only-profile.svg" - end - end - end + def enterprise_category + object.enterprise_category end - # TODO: Remove this when flags on enterprises are switched over def has_shopfront - object.type != 'profile' + object.is_distributor && object.type != 'profile' end - # TODO: Remove this when flags on enterprises are switched over - def can_aggregate - object.is_distributor && object.suppliers != [object] + # Used to select enterprises for hub listing + def has_hub_listing + has_shopfront || object.enterprise_category == "hub_profile" end + + # Map svg icons. + def icon + icons = { + "hub" => "/assets/map_005-hub.svg", + "hub_profile" => "/assets/map_006-hub-profile.svg", + "producer_hub" => "/assets/map_005-hub.svg", + "producer_shop" => "/assets/map_003-producer-shop.svg", + "producer" => "/assets/map_001-producer-only.svg", + "producer_profile" => "/assets/map_002-producer-only-profile.svg", + } + icons[object.enterprise_category] + end + + # Choose regular icon font for enterprises. + def icon_font + icon_fonts = { + "hub" => "ofn-i_063-hub", + "hub_profile" => "ofn-i_064-hub-reversed", + "producer_hub" => "ofn-i_063-hub", + "producer_shop" => "ofn-i_059-producer", + "producer" => "ofn-i_059-producer", + "producer_profile" => "ofn-i_060-producer-reversed", + } + icon_fonts[object.enterprise_category] + end + + # Choose producer page icon font - yes, sadly its got to be different. + # This duplicates some code but covers the producer page edge case where + # producer-hub has a producer icon without needing to duplicate the category logic in angular. + def producer_icon_font + icon_fonts = { + "hub" => "", + "hub_profile" => "", + "producer_hub" => "ofn-i_059-producer", + "producer_shop" => "ofn-i_059-producer", + "producer" => "ofn-i_059-producer", + "producer_profile" => "ofn-i_060-producer-reversed", + "empty" => "", + } + icon_fonts[object.enterprise_category] + end + end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer diff --git a/app/views/home/_filters.html.haml b/app/views/home/_filters.html.haml index 78d755f1bc..ff13a0c33c 100644 --- a/app/views/home/_filters.html.haml +++ b/app/views/home/_filters.html.haml @@ -1,23 +1,22 @@ -= render partial: 'shared/components/filter_controls' +.row + = render partial: 'shared/components/filter_controls' + = render partial: 'shared/components/show_profiles' .row.animate-show{"ng-show" => "filtersActive"} - .small-12.columns + .small-12.columns .row.filter-box .small-12.large-9.columns - %h5.tdhead + %h5.tdhead .light Filter by Type %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-5 - %taxon-selector{objects: "hubs | hubs:query", + %taxon-selector{objects: "hubs | hubs:query", results: "activeTaxons"} .small-12.large-3.columns - %h5.tdhead + %h5.tdhead .light Filter by Delivery %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-2 - %shipping-type-selector{results: "shippingTypes"} - .row.filter-box.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} - .small-12.columns - %a.button.secondary.small.expand{"ng-click" => "clearAll()"} - %i.ofn-i_009-close - Clear all filters + %shipping-type-selector{results: "shippingTypes"} + += render partial: 'shared/components/filter_box' diff --git a/app/views/home/_hubs.html.haml b/app/views/home/_hubs.html.haml index ff90a45d48..67cb6abb40 100644 --- a/app/views/home/_hubs.html.haml +++ b/app/views/home/_hubs.html.haml @@ -1,19 +1,13 @@ -= inject_enterprises += inject_enterprises #hubs.hubs{"ng-controller" => "HubsCtrl"} .row .small-12.columns - %h1 Shop your local area - / %div - / Shop a - / %ofn-modal{title: "food hub"} - / = render partial: "modals/food_hub" - / from the list below: + %h1 Shop in your local area #active-table-search.row.pad-top .small-12.columns - / %i.ofn-i_020-search - %input{type: :text, - "ng-model" => "query", + %input{type: :text, + "ng-model" => "query", placeholder: "Search by name or suburb...", "ng-debounce" => "150", "ofn-disable-enter" => true} @@ -23,11 +17,11 @@ .row{bindonce: true} .small-12.columns .active_table - %hub.active_table_node.row.animate-repeat{"ng-repeat" => "hub in filteredHubs = (hubs | hubs:query | taxons:activeTaxons | shipping:shippingTypes)", - "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", + %hub.active_table_node.row.animate-repeat{"ng-repeat" => "hub in filteredHubs = (hubs | hubs:query | taxons:activeTaxons | shipping:shippingTypes | showProfiles:show_profiles )", + "ng-class" => "{'is_profile' : !hub.has_shopfront, 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "scroll-after-load" => true, "ng-controller" => "HubNodeCtrl", - id: "{{hub.hash}}"} + id: "{{hub.hash}}"} .small-12.columns = render partial: 'home/skinny' = render partial: 'home/fat' diff --git a/app/views/home/_skinny.html.haml b/app/views/home/_skinny.html.haml index da9709479b..2010bffe1e 100644 --- a/app/views/home/_skinny.html.haml +++ b/app/views/home/_skinny.html.haml @@ -1,12 +1,10 @@ -.row.active_table_row{"ng-click" => "toggle()", "ng-class" => "{'closed' : !open()}", bindonce: true} +.row.active_table_row{"ng-if" => "hub.has_shopfront", "ng-click" => "toggle()", "ng-class" => "{'closed' : !open(), 'has_shopfront' : producer.has_shopfront}", bindonce: true} + .columns.small-12.medium-6.large-5.skinny-head %a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} - %i{ ng: { class: "{ 'ofn-i_063-hub': hub.can_aggregate && hub.has_shopfront, - 'ofn-i_059-producer': !hub.can_aggregate && hub.has_shopfront, - 'ofn-i_060-producer-reversed': !hub.can_aggregate && !hub.has_shopfront, - 'ofn-i_064-hub-reversed': hub.can_aggregate && !hub.has_shopfront }" } } - / %i.ofn-i_063-hub + %i{ng: {class: "hub.icon_font"}} %span.margin-top.hub-name-listing {{ hub.name | truncate:40}} + .columns.small-4.medium-2.large-2 %span.margin-top {{ hub.address.city }} .columns.small-2.medium-1.large-1 @@ -15,16 +13,28 @@ .columns.small-6.medium-3.large-4.text-right{"bo-if" => "hub.active"} %a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} %i.ofn-i_033-open-sign - %span.margin-top{ bo: { if: "current()" } } + %span.margin-top{ bo: { if: "current()" } } %em Shopping here %span.margin-top{ bo: { if: "!current()" } } {{ hub.orders_close_at | sensible_timeframe }} .columns.small-6.medium-3.large-4.text-right{"bo-if" => "!hub.active"} %a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} %i.ofn-i_032-closed-sign - %span.margin-top{ bo: { if: "current()" } } + %span.margin-top{ bo: { if: "current()" } } %em Shopping here %span.margin-top{ bo: { if: "!current()" } } Orders closed +.row.active_table_row{"ng-if" => "!hub.has_shopfront", "ng-class" => "closed"} + .columns.small-12.medium-6.large-5.skinny-head + %a.hub{"ng-click" => "openModal(hub)", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} + %i{ng: {class: "hub.icon_font"}} + %span.margin-top.hub-name-listing {{ hub.name | truncate:40}} + .columns.small-4.medium-2.large-2 + %span.margin-top {{ hub.address.city }} + .columns.small-2.medium-1.large-1 + %span.margin-top {{ hub.address.state_name | uppercase }} + .columns.small-6.medium-3.large-4.text-right + %span.margin-top{ bo: { if: "!current()" } } + %em Profile only diff --git a/app/views/producers/_filters.html.haml b/app/views/producers/_filters.html.haml index 63ccef2f0c..92f0870e7e 100644 --- a/app/views/producers/_filters.html.haml +++ b/app/views/producers/_filters.html.haml @@ -1,18 +1,15 @@ -= render partial: 'shared/components/filter_controls' +.row + = render partial: 'shared/components/filter_controls' + .small-12.medium-6.columns.text-right +   .row.animate-show{"ng-show" => "filtersActive"} .small-12.columns .row.filter-box .small-12.columns - %h5.tdhead + %h5.tdhead .light Filter by Type %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-6 - %taxon-selector{objects: "Producers.visible | filterProducers:query", + %taxon-selector{objects: "Producers.visible | filterProducers:query ", results: "activeTaxons"} - - .row.filter-box.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} - .small-12.columns - %a.button.secondary.small.expand{"ng-click" => "clearAll()"} - %i.ofn-i_009-close - Clear all filters diff --git a/app/views/producers/_skinny.html.haml b/app/views/producers/_skinny.html.haml index a5d6b790fc..37767b3789 100644 --- a/app/views/producers/_skinny.html.haml +++ b/app/views/producers/_skinny.html.haml @@ -1,9 +1,16 @@ -.row.active_table_row{"ng-click" => "toggle()", "ng-class" => "{'closed' : !open()}"} +.row.active_table_row{"ng-click" => "toggle()", "ng-class" => "{'closed' : !open(), 'has_shopfront' : producer.has_shopfront}"} .columns.small-12.medium-4.large-4.skinny-head - / This needs logic to show profile only icon when available %i.ofn-i_060-producer-reversed - %i.ofn-i_059-producer - %span.margin-top - %strong {{ producer.name }} + %span{"bo-if" => "producer.has_shopfront" } + %a.has_shopfront{"bo-href" => "producer.path" } + %i{ng: {class: "producer.producer_icon_font"}} + %span.margin-top + %strong {{ producer.name }} + %span.producer-name{"bo-if" => "!producer.has_shopfront" } + %i{ng: {class: "producer.producer_icon_font"}} + %span.margin-top + %strong {{ producer.name }} + + .columns.small-6.medium-3.large-3 %span.margin-top {{ producer.address.city }} .columns.small-4.medium-3.large-4 diff --git a/app/views/producers/index.html.haml b/app/views/producers/index.html.haml index 76dc60a7e6..fe487585e6 100644 --- a/app/views/producers/index.html.haml +++ b/app/views/producers/index.html.haml @@ -1,18 +1,18 @@ -= inject_enterprises += inject_enterprises .producers.pad-top{"ng-controller" => "ProducersCtrl"} .row .small-12.columns.pad-top %h1 Find local producers - / %div - / Find a + / %div + / Find a / %ofn-modal{title: "producer"} / = render partial: "modals/producers" / from the list below: #active-table-search.row .small-12.columns - %input.animate-show{type: :text, - "ng-model" => "query", + %input.animate-show{type: :text, + "ng-model" => "query", placeholder: "Search by producer or suburb...", "ng-debounce" => "150", "ofn-disable-enter" => true} diff --git a/app/views/registration/limit_reached.html.haml b/app/views/registration/limit_reached.html.haml new file mode 100644 index 0000000000..bfaec6da3d --- /dev/null +++ b/app/views/registration/limit_reached.html.haml @@ -0,0 +1,2 @@ +/ Directive which loads the modal +%div{ "ofn-registration-limit-modal" => true } diff --git a/app/views/shared/components/_filter_box.html.haml b/app/views/shared/components/_filter_box.html.haml new file mode 100644 index 0000000000..0256a57ff1 --- /dev/null +++ b/app/views/shared/components/_filter_box.html.haml @@ -0,0 +1,5 @@ +.row.filter-box.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} + .small-12.columns + %a.button.secondary.small.expand{"ng-click" => "clearAll()"} + %i.ofn-i_009-close + Clear all filters diff --git a/app/views/shared/components/_filter_controls.html.haml b/app/views/shared/components/_filter_controls.html.haml index 4548085550..e15d2de48d 100644 --- a/app/views/shared/components/_filter_controls.html.haml +++ b/app/views/shared/components/_filter_controls.html.haml @@ -1,19 +1,9 @@ -.row - .small-12.medium-6.columns - %a.button.success.tiny.filterbtn{"ng-click" => "filtersActive = !filtersActive", - "ng-show" => "FilterSelectorsService.selectors.length > 0"} - {{ filterText(filtersActive) }} - %i.ofn-i_005-caret-down{"ng-show" => "!filtersActive"} - %i.ofn-i_006-caret-up{"ng-show" => "filtersActive"} +.small-12.medium-6.columns + %a.button.success.tiny.filterbtn{"ng-click" => "filtersActive = !filtersActive", + "ng-show" => "FilterSelectorsService.selectors.length > 0"} + {{ filterText(filtersActive) }} + %i.ofn-i_005-caret-down{"ng-show" => "!filtersActive"} + %i.ofn-i_006-caret-up{"ng-show" => "filtersActive"} - %a.button.secondary.tiny.filterbtn.disabled{"ng-show" => "FilterSelectorsService.selectors.length == 0"} - No filters - .small-12.medium-6.columns.text-right - .profile-checkbox - - / Hide until we're ready to work on this: - - / %input{type: "checkbox", name: "profile"}>< - / %label Show profiles - / %button.button.secondary.tiny.help-btn.ng-scope{:popover => "Profiles do not have a shopfront on the Open Food Network, but they may have their own physical or online shop elsewhere", "popover-placement" => "left"}>< - / %i.ofn-i_013-help + %a.button.secondary.tiny.filterbtn.disabled{"ng-show" => "FilterSelectorsService.selectors.length == 0"} + No filters diff --git a/app/views/shared/components/_show_profiles.html.haml b/app/views/shared/components/_show_profiles.html.haml new file mode 100644 index 0000000000..c2232c3555 --- /dev/null +++ b/app/views/shared/components/_show_profiles.html.haml @@ -0,0 +1,6 @@ +.small-12.medium-6.columns.text-right + .profile-checkbox + %input{"ng-model" => "show_profiles", type: "checkbox", name: "profile"}>< + %label Show profiles + %button.button.secondary.tiny.help-btn.ng-scope{:popover => "Profiles do not have a shopfront on the Open Food Network, but may have their own physical or online shop elsewhere", "popover-placement" => "left"}>< + %i.ofn-i_013-help diff --git a/app/views/shop/products/_filters.html.haml b/app/views/shop/products/_filters.html.haml index c57f927e61..c135274fad 100644 --- a/app/views/shop/products/_filters.html.haml +++ b/app/views/shop/products/_filters.html.haml @@ -1,18 +1,17 @@ -= render partial: 'shared/components/filter_controls' +.row + = render partial: 'shared/components/filter_controls' + .small-12.medium-6.columns.text-right +   .row.animate-show{"ng-show" => "filtersActive"} .small-12.columns .row.filter-box .small-12.columns - %h5.tdhead + %h5.tdhead .light Filter by Type %ul.small-block-grid-2.medium-block-grid-3.large-block-grid-4 - %taxon-selector{objects: "Products.products | products:query", + %taxon-selector{objects: "Products.products | products:query | products:showProfiles", results: "activeTaxons"} - .row.filter-box.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} - .small-12.columns - %a.button.secondary.small.expand{"ng-click" => "clearAll()"} - %i.ofn-i_009-close - Clear all filters += render partial: 'shared/components/filter_box' diff --git a/app/views/spree/order_mailer/confirm_email.text.haml b/app/views/spree/order_mailer/confirm_email.text.haml index 0911228c66..c9ad50fb63 100644 --- a/app/views/spree/order_mailer/confirm_email.text.haml +++ b/app/views/spree/order_mailer/confirm_email.text.haml @@ -13,7 +13,7 @@ Subtotal: #{number_to_currency checkout_cart_total_with_adjustments(@order)} - checkout_adjustments_for_summary(@order, exclude: [:distribution]).each do |adjustment| #{raw(adjustment.label)} #{adjustment.display_amount} Order Total: #{@order.display_total} -- if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" +- if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description \ ============================================================ Payment Details @@ -28,6 +28,9 @@ Order Total: #{@order.display_total} Your order will be delivered to: #{@order.ship_address.to_s} + - if @order.shipping_method.andand.description + #{@order.shipping_method.description.html_safe} + - if @order.order_cycle.andand.pickup_time_for(@order.distributor) Delivery on: #{@order.order_cycle.pickup_time_for(@order.distributor)} diff --git a/spec/controllers/registration_controller_spec.rb b/spec/controllers/registration_controller_spec.rb index 13babdf89e..e51a28f73a 100644 --- a/spec/controllers/registration_controller_spec.rb +++ b/spec/controllers/registration_controller_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe RegistrationController do + include AuthenticationWorkflow describe "redirecting when user not logged in" do it "index" do get :index @@ -13,26 +14,38 @@ describe RegistrationController do end end - describe "loading data when user is logged in" do - let!(:user) { double(:user) } + describe "redirecting when user has reached enterprise ownership limit" do + let!(:user) { create_enterprise_user( enterprise_limit: 1 ) } + let!(:enterprise) { create(:distributor_enterprise, owner: user) } + + before do + controller.stub spree_current_user: user + end + + it "index" do + get :index + response.should render_template :limit_reached + end + end + + describe "loading data when user is logged in" do + let!(:user) { create_enterprise_user } before do controller.stub spree_current_user: user - user.stub spree_api_key: '12345' - user.stub last_incomplete_spree_order: nil end describe "index" do it "loads the spree api key" do get :index - expect(assigns(:spree_api_key)).to eq '12345' + expect(assigns(:spree_api_key)).to eq user.spree_api_key end end describe "store" do it "loads the spree api key" do get :store - expect(assigns(:spree_api_key)).to eq '12345' + expect(assigns(:spree_api_key)).to eq user.spree_api_key end end end diff --git a/spec/features/admin/enterprise_relationships_spec.rb b/spec/features/admin/enterprise_relationships_spec.rb index bfc624bce5..5e4366b6f7 100644 --- a/spec/features/admin/enterprise_relationships_spec.rb +++ b/spec/features/admin/enterprise_relationships_spec.rb @@ -89,7 +89,7 @@ feature %q{ let!(:d1) { create(:distributor_enterprise) } let!(:d2) { create(:distributor_enterprise) } let!(:d3) { create(:distributor_enterprise) } - let(:enterprise_user) { create_enterprise_user([d1]) } + let(:enterprise_user) { create_enterprise_user( enterprises: [d1] ) } let!(:er1) { create(:enterprise_relationship, parent: d1, child: d2) } let!(:er2) { create(:enterprise_relationship, parent: d2, child: d1) } diff --git a/spec/features/admin/enterprise_user_spec.rb b/spec/features/admin/enterprise_user_spec.rb index 77378f6cbd..70e6a9b265 100644 --- a/spec/features/admin/enterprise_user_spec.rb +++ b/spec/features/admin/enterprise_user_spec.rb @@ -64,7 +64,7 @@ feature %q{ page.should have_admin_menu_item 'Dashboard' page.should have_admin_menu_item 'Enterprises' - ['Orders', 'Products', 'Reports', 'Configuration', 'Promotions', 'Users', 'Order Cycles'].each do |menu_item_name| + ['Orders', 'Reports', 'Configuration', 'Promotions', 'Users', 'Order Cycles'].each do |menu_item_name| page.should_not have_admin_menu_item menu_item_name end end @@ -79,15 +79,15 @@ feature %q{ end end - it "does not show me product management controls" do - page.should_not have_selector '#products' + it "shows me product management controls, but not order_cycle controls" do + page.should have_selector '#products' page.should_not have_selector '#order_cycles' end - it "does not show me enterprise product info, payment methods, shipping methods or enterprise fees" do + it "shows me enterprise product info but not payment methods, shipping methods or enterprise fees" do # Producer product info - page.should_not have_selector '.producers_tab span', text: 'Total Products' - page.should_not have_selector '.producers_tab span', text: 'Active Products' + page.should have_selector '.producers_tab span', text: 'Total Products' + page.should have_selector '.producers_tab span', text: 'Active Products' page.should_not have_selector '.producers_tab span', text: 'Products in OCs' # Payment methods, shipping methods, enterprise fees diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index e880f25d23..5228b4c8a0 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -10,8 +10,8 @@ feature %q{ context "Permissions for different reports" do context "As an enterprise user" do let(:user) do - create_enterprise_user([ - create(:distributor_enterprise) + create_enterprise_user(enterprises: [ + create(:distributor_enterprise) ]) end it "should not show the Sales Total report" do @@ -99,7 +99,7 @@ feature %q{ let(:shipping_instructions) { "pick up on thursday please!" } let(:order1) { create(:order, :distributor => distributor, :bill_address => bill_address, :special_instructions => shipping_instructions) } let(:order2) { create(:order, :distributor => distributor, :bill_address => bill_address, :special_instructions => shipping_instructions) } - + before do Timecop.travel(Time.zone.local(2013, 4, 25, 14, 0, 0)) { order1.finalize! } Timecop.travel(Time.zone.local(2013, 4, 25, 16, 0, 0)) { order2.finalize! } @@ -144,7 +144,7 @@ feature %q{ variant_2.update_column(:count_on_hand, 20) product_2.master.update_column(:count_on_hand, 9) variant_1.option_values = [create(:option_value, :presentation => "Test")] - + login_to_admin_section click_link 'Reports' @@ -165,4 +165,3 @@ feature %q{ end end end - diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index 8c611081a2..7d9a7350b5 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -5,6 +5,7 @@ //= require angular-mocks //= require angular-cookies //= require angular-backstretch.js +//= require angularjs-file-upload //= require lodash.underscore.js //= require angular-flash.min.js //= require shared/mm-foundation-tpls-0.2.2.min.js diff --git a/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee index f8ae8230bc..f2620da65b 100644 --- a/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee @@ -1,46 +1,45 @@ describe "Hubs service", -> Hubs = null Enterprises = null - CurrentHubMock = {} + CurrentHubMock = {} hubs = [ { id: 2 active: false orders_close_at: new Date() is_distributor: true - has_shopfront: true + has_hub_listing: true } { id: 3 active: false orders_close_at: new Date() is_distributor: true - has_shopfront: true + has_hub_listing: true } { id: 1 active: true orders_close_at: new Date() is_distributor: true - has_shopfront: true + has_hub_listing: true } ] - + beforeEach -> module 'Darkswarm' - angular.module('Darkswarm').value('enterprises', hubs) + angular.module('Darkswarm').value('enterprises', hubs) module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock + $provide.value "CurrentHub", CurrentHubMock null inject ($injector)-> - Enterprises = $injector.get("Enterprises") + Enterprises = $injector.get("Enterprises") Hubs = $injector.get("Hubs") it "filters Enterprise.hubs into a new array", -> expect(Hubs.hubs[0]).toBe Enterprises.enterprises[2] - # Because the $filter is a new sorted array + # Because the $filter is a new sorted array # We check to see the objects in both arrays are still the same - Enterprises.enterprises[2].active = false + Enterprises.enterprises[2].active = false expect(Hubs.hubs[0].active).toBe false - diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index f6b3c13d2d..68067b5ac5 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -501,4 +501,65 @@ describe Enterprise do supplier.producer_properties.first.property.presentation.should == 'Organic Certified' end end + + pending "provide enterprise category" do + + # Swap type values full > sell_all, single > sell_own profile > sell_none + # swap is_distributor for new can_supply flag. + let(:producer_sell_all_can_supply) { + create(:enterprise, is_primary_producer: true, type: "full", is_distributor: true) + } + let(:producer_sell_all_cant_supply) { + create(:enterprise, is_primary_producer: true, type: "full", is_distributor: false) + } + let(:producer_sell_own_can_supply) { + create(:enterprise, is_primary_producer: true, type: "single", is_distributor: true) + } + let(:producer_sell_own_cant_supply) { + create(:enterprise, is_primary_producer: true, type: "single", is_distributor: false) + } + let(:producer_sell_none_can_supply) { + create(:enterprise, is_primary_producer: true, type: "profile", is_distributor: true) + } + let(:producer_sell_none_cant_supply) { + create(:enterprise, is_primary_producer: true, type: "profile", is_distributor: false) + } + let(:non_producer_sell_all_can_supply) { + create(:enterprise, is_primary_producer: true, type: "full", is_distributor: true) + } + let(:non_producer_sell_all_cant_supply) { + create(:enterprise, is_primary_producer: true, type: "full", is_distributor: false) + } + let(:non_producer_sell_own_can_supply) { + create(:enterprise, is_primary_producer: true, type: "single", is_distributor: true) + } + let(:non_producer_sell_own_cant_supply) { + create(:enterprise, is_primary_producer: true, type: "single", is_distributor: false) + } + let(:non_producer_sell_none_can_supply) { + create(:enterprise, is_primary_producer: false, type: "profile", is_distributor: true) + } + let(:non_producer_sell_none_cant_supply) { + create(:enterprise, is_primary_producer: false, type: "profile", is_distributor: false) + } + + it "should output enterprise categories" do + producer_sell_all_can_supply.is_primary_producer.should == true + producer_sell_all_can_supply.supplies.should == true + producer_sell_all_can_supply.type.should == "full" + + producer_sell_all_can_supply.enterprise_category.should == "producer_hub" + producer_sell_all_cant_supply.enterprise_category.should == "producer_hub" + producer_sell_own_can_supply.enterprise_category.should == "producer_shop" + producer_sell_own_cant_supply.enterprise_category.should == "producer_shop" + producer_sell_none_can_supply.enterprise_category.should == "producer" + producer_sell_none_cant_supply.enterprise_category.should == "producer_profile" + non_producer_sell_all_can_supply.enterprise_category.should == "hub" + non_producer_sell_all_cant_supply.enterprise_category.should == "hub" + non_producer_sell_own_can_supply.enterprise_category.should == "hub" + non_producer_sell_own_cant_supply.enterprise_category.should == "hub" + non_producer_sell_none_can_supply.enterprise_category.should == "hub_profile" + non_producer_sell_none_cant_supply.enterprise_category.should == "hub_profile" + end + end end diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 78ca1db909..2d2d1cfb01 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -13,44 +13,46 @@ module Spree let(:enterprise_single) { create(:enterprise, type: 'single') } let(:enterprise_profile) { create(:enterprise, type: 'profile') } - describe "creating enterprises" do + context "as manager of a 'full' type enterprise" do + before do + user.enterprise_roles.create! enterprise: enterprise_full + end + + it { subject.can_manage_products?(user).should be_true } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_true } + end + + context "as manager of a 'single' type enterprise" do + before do + user.enterprise_roles.create! enterprise: enterprise_single + end + + it { subject.can_manage_products?(user).should be_true } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_true } + end + + context "as manager of a 'profile' type enterprise" do + before do + user.enterprise_roles.create! enterprise: enterprise_profile + end + + it { subject.can_manage_products?(user).should be_true } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_false } + end + + context "as a new user with no enterprises" do + it { subject.can_manage_products?(user).should be_false } + it { subject.can_manage_enterprises?(user).should be_false } + it { subject.can_manage_orders?(user).should be_false } + it "can create enterprises straight off the bat" do subject.is_new_user?(user).should be_true expect(user).to have_ability :create, for: Enterprise end end - - describe "managing enterprises" do - it "can manage enterprises when the user has at least one enterprise assigned" do - user.enterprise_roles.create! enterprise: enterprise_full - subject.can_manage_enterprises?(user).should be_true - end - - it "can't otherwise" do - subject.can_manage_enterprises?(user).should be_false - end - end - - describe "managing products" do - it "can when a user manages a 'full' type enterprise" do - user.enterprise_roles.create! enterprise: enterprise_full - subject.can_manage_products?(user).should be_true - end - - it "can when a user manages a 'single' type enterprise" do - user.enterprise_roles.create! enterprise: enterprise_single - subject.can_manage_products?(user).should be_true - end - - it "can't when a user manages a 'profile' type enterprise" do - user.enterprise_roles.create! enterprise: enterprise_profile - subject.can_manage_products?(user).should be_false - end - - it "can't when the user manages no enterprises" do - subject.can_manage_products?(user).should be_false - end - end end describe 'Roles' do diff --git a/spec/support/request/authentication_workflow.rb b/spec/support/request/authentication_workflow.rb index a2ee597f27..39f78223d7 100644 --- a/spec/support/request/authentication_workflow.rb +++ b/spec/support/request/authentication_workflow.rb @@ -37,12 +37,9 @@ module AuthenticationWorkflow visit spree.admin_path end - def create_enterprise_user(enterprises = []) - new_user = create(:user, password: 'blahblah', :password_confirmation => 'blahblah') + def create_enterprise_user( attrs = {} ) + new_user = create(:user, attrs) new_user.spree_roles = [] # for some reason unbeknown to me, this new user gets admin permissions by default. - for enterprise in enterprises do - new_user.enterprise_roles.build(enterprise: enterprise).save - end new_user.save new_user end