From 586753015b2fe309cc03fbdcdd53f9248c7a4fa1 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 18 Dec 2014 10:25:01 +1100 Subject: [PATCH 001/384] Reducing the groups page to a oneline list of groups --- .../stylesheets/darkswarm/groups.css.sass | 40 +++++-------------- app/views/groups/index.html.haml | 38 ++++-------------- 2 files changed, 16 insertions(+), 62 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index b859273ab7..4686c452a2 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -9,34 +9,12 @@ padding-bottom: 20px .group - padding-bottom: 40px - hr - border-bottom: 10px solid white - outline: 0 - border-top: 0 - margin: 0 - -.group-hero - position: relative - padding: 0 - border: 10px solid white - background: white - -h3.group-name - margin-top: 0.5em - margin-bottom: 0.15em - -img.group-logo - max-width: 220px - max-height: 86px - float: right - padding-top: 10px - - -img.group-hero-img - background-color: black - width: 100% - height: inherit - max-height: 260px - min-height: 120px - overflow: hidden \ No newline at end of file + padding-bottom: 0.5em + .row div + font-size: 110% + .row a + font-weight: 500 + vertical-align: middle + .ofn-i_035-groups + font-size: 120% + vertical-align: middle diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 1aa5b02629..b085bb446e 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -5,50 +5,26 @@ #groups{"ng-controller" => "GroupsCtrl"} #active-table-search.row.pad-top - .small-12.columns.text-center - %h1 Groups / Regions - %div - Check out our - %ofn-modal{title: "food groups"} - = render partial: "modals/groups" - below + .small-12.columns + %h1 Groups / regions %p %input.animate-show{type: :text, "ng-model" => "query", - placeholder: "Search group name", + placeholder: "Search name or keyword", "ng-debounce" => "150", "ofn-disable-enter" => true} .group{"ng-repeat" => "group in groups = (Groups.groups | groups:query | orderBy:order)", name: "group{{group.id}}", id: "group{{group.id}}"} - .row.pad-top{bindonce: true} - .small-12.columns - .group-hero - %img.group-hero-img{"bo-src" => "group.promo_image"} - %img.group-logo{"bo-src" => "group.logo", "bo-if" => "group.logo"} - %h3.group-name - %i.ofn-i_035-groups - {{ group.name }} - %h5.group-description {{ group.description }} - .row.pad-top{bindonce: true} .small-6.columns - %p {{ group.long_description }} + %a{"ng-href" => "group/{{group.id}}"} + %i.ofn-i_035-groups + {{ group.name }} .small-6.columns - %h5 Our hubs & producers - %ul.small-block-grid-2 - %li{"ng-repeat" => "enterprise in group.enterprises", "scroll-after-load" => true} - %enterprise-modal{"ng-if" => "enterprise.is_distributor"} - {{ enterprise.name }} - %enterprise-modal{"ng-if" => "!enterprise.is_distributor", "show-hub-actions" => 'true'} - {{ enterprise.name }} - - - .row.group_footer - .small-12.columns - %hr + {{ group.description }} .group{"ng-show" => "groups.length == 0"} .row.pad-top From 9798b05a24c90e5bb6ab05138fc192acc4f53408 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 18 Dec 2014 15:42:56 +1100 Subject: [PATCH 002/384] TabsCtrl can show tabs that don't toggle toggle: tabs show on first click and hide on second click select: tabs show on every click, one tab is always active --- .../darkswarm/controllers/tabs_controller.js.coffee | 8 ++++++-- app/views/shopping_shared/_tabs.html.haml | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee index 0bbe0bbd23..74ce167580 100644 --- a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee @@ -1,10 +1,14 @@ -Darkswarm.controller "TabsCtrl", ($scope, $rootScope, $location, OrderCycle) -> +Darkswarm.controller "TabsCtrl", ($scope, $rootScope, $location) -> # Return active if supplied path matches url hash path. $scope.active = (path)-> $location.hash() == path + # Select tab by setting the url hash path. + $scope.select= (path)-> + $location.hash path + # Toggle tab selected status by setting the url hash path. - $scope.select = (path)-> + $scope.toggle = (path)-> if $scope.active(path) $location.hash "" else diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index d136efa045..90ecc663ea 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -11,6 +11,6 @@ %tab.columns{heading: heading, id: "tab_#{name}", active: "active(\'#{name}\')", - select: "select(\'#{name}\')", + select: "toggle(\'#{name}\')", class: "small-12 medium-#{cols}" } = render "shopping_shared/#{name}" From 9310bc902a26b0914ca775627ad1aae87a616247 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 18 Dec 2014 16:00:54 +1100 Subject: [PATCH 003/384] first draft of group page, a lot of TODOs --- app/controllers/groups_controller.rb | 4 +++ app/views/groups/show.html.haml | 42 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 app/views/groups/show.html.haml diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 504bfa9569..8653131b5a 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -5,4 +5,8 @@ class GroupsController < BaseController def index @groups = EnterpriseGroup.on_front_page.by_position end + + def show + @group = EnterpriseGroup.find params[:id] + end end diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml new file mode 100644 index 0000000000..1d0cf8b21b --- /dev/null +++ b/app/views/groups/show.html.haml @@ -0,0 +1,42 @@ +%div{style: "padding: 1.5em;"} + %img{"src" => @group.promo_image, style: "display: block"} + %div{style: "margin-bottom: 1em"} + %img{"src" => @group.logo} + %div{style: "display: inline-block; vertical-align: middle"} + %h2{style: "margin-bottom: 0em"}= @group.name + %div= @group.description + + #div{"ng-controller" => "TabsCtrl", style: "clear: both"} + %tabset + %tab{heading: 'Map', + active: "active(\'\')", + select: "select(\'\')"} + %p TODO: show a map of enterprises + %tab{heading: 'About us', + active: "active(\'hi\')", + select: "select(\'hi\')"} + %h3 About us + %p= @group.long_description + %tab{heading: 'Our producers', + active: "active(\'producers\')", + select: "select(\'producers\')"} + %h3 Our producers + %ul + - @group.enterprises.is_primary_producer.each do |enterprise| + %li= link_to enterprise.name, enterprise + %tab{heading: 'Our hubs', + active: "active(\'hubs\')", + select: "select(\'hubs\')"} + %h3 Our producers + %ul + - @group.enterprises.is_distributor.each do |enterprise| + %li= link_to enterprise.name, enterprise + %tab{heading: 'Contact us', + active: "active(\'contact\')", + select: "select(\'contact\')"} + %h3 Contact us + %p TODO: provide contact information + + %p TODO: show the group's contact footer + += render partial: "shared/footer" From 9956b967f0e59a1e184e9f462c48ddea65834852 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 7 Jan 2015 10:27:08 +1100 Subject: [PATCH 004/384] Fixing link to group's page --- app/views/groups/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index b085bb446e..d9297d7202 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -20,7 +20,7 @@ id: "group{{group.id}}"} .row.pad-top{bindonce: true} .small-6.columns - %a{"ng-href" => "group/{{group.id}}"} + %a{"ng-href" => "groups/{{group.id}}"} %i.ofn-i_035-groups {{ group.name }} .small-6.columns From 3b9657eb1726ab221de55838418253a6f0d5c0d7 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 7 Jan 2015 12:00:55 +1100 Subject: [PATCH 005/384] show map of a group's enterprises --- app/views/groups/show.html.haml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 1d0cf8b21b..0d5fe29359 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -12,6 +12,12 @@ active: "active(\'\')", select: "select(\'\')"} %p TODO: show a map of enterprises + = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer + .map-container + %map{"ng-controller" => "MapCtrl"} + %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} + %markers{models: "OfnMap.enterprises", fit: "true", + coords: "'self'", icon: "'icon'", click: "'reveal'"} %tab{heading: 'About us', active: "active(\'hi\')", select: "select(\'hi\')"} From 0e510998a490915512bbc581d1e720c6f40aaf57 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 14 Jan 2015 14:33:37 +1100 Subject: [PATCH 006/384] Handling missing options in shipping filter call --- .../javascripts/darkswarm/filters/shipping.js.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/darkswarm/filters/shipping.js.coffee b/app/assets/javascripts/darkswarm/filters/shipping.js.coffee index 761e34337d..7879568f0d 100644 --- a/app/assets/javascripts/darkswarm/filters/shipping.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/shipping.js.coffee @@ -1,9 +1,10 @@ -Darkswarm.filter 'shipping', ()-> +Darkswarm.filter 'shipping', ()-> (objects, options)-> objects ||= [] - options ?= null - - if options.pickup and !options.delivery + + if !options + objects + else if options.pickup and !options.delivery objects.filter (obj)-> obj.pickup else if options.delivery and !options.pickup From 9f1a773a58b28772d576c88f2bd6de961f60ee16 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 14 Jan 2015 14:34:31 +1100 Subject: [PATCH 007/384] MapController: copy default config instead of referencing Copying makes it possible to have multiple independent maps. --- .../javascripts/darkswarm/controllers/map_controller.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/darkswarm/controllers/map_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/map_controller.js.coffee index 7b0cd3607b..853e86d510 100644 --- a/app/assets/javascripts/darkswarm/controllers/map_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/map_controller.js.coffee @@ -1,3 +1,3 @@ Darkswarm.controller "MapCtrl", ($scope, MapConfiguration, OfnMap)-> $scope.OfnMap = OfnMap - $scope.map = MapConfiguration.options + $scope.map = angular.copy MapConfiguration.options From 02a276b9c91eecb64177fbfbc2209c0783920619 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 14 Jan 2015 15:18:46 +1100 Subject: [PATCH 008/384] Producer and hub lists with search Fixing map display as well. --- ...group_enterprise_node_controller.js.coffee | 12 ++++ .../group_enterprises_controller.js.coffee | 13 ++++ app/views/groups/show.html.haml | 63 +++++++++++++++---- 3 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/controllers/group_enterprise_node_controller.js.coffee create mode 100644 app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/group_enterprise_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/group_enterprise_node_controller.js.coffee new file mode 100644 index 0000000000..31c2354a53 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/group_enterprise_node_controller.js.coffee @@ -0,0 +1,12 @@ +Darkswarm.controller "GroupEnterpriseNodeCtrl", ($scope, CurrentHub) -> + + $scope.active = false + + $scope.toggle = () -> + $scope.active = !$scope.active + + $scope.open = -> + $scope.active + + $scope.current = -> + $scope.hub.id is CurrentHub.hub.id diff --git a/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee new file mode 100644 index 0000000000..ab8336116f --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee @@ -0,0 +1,13 @@ +Darkswarm.controller "GroupEnterprisesCtrl", ($scope, Enterprises, Search, FilterSelectorsService) -> + $scope.Enterprises = Enterprises + $scope.totalActive = FilterSelectorsService.totalActive + $scope.clearAll = FilterSelectorsService.clearAll + $scope.filterText = FilterSelectorsService.filterText + $scope.FilterSelectorsService = FilterSelectorsService + $scope.query = Search.search() + $scope.activeTaxons = [] + $scope.show_profiles = false + $scope.filtersActive = false + + $scope.$watch "query", (query)-> + Search.search query diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 0d5fe29359..2ec509c0c5 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -8,35 +8,74 @@ #div{"ng-controller" => "TabsCtrl", style: "clear: both"} %tabset + %tab{heading: 'Map', active: "active(\'\')", select: "select(\'\')"} - %p TODO: show a map of enterprises = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer .map-container - %map{"ng-controller" => "MapCtrl"} + %map{"ng-controller" => "MapCtrl", "ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} %markers{models: "OfnMap.enterprises", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} + %tab{heading: 'About us', - active: "active(\'hi\')", - select: "select(\'hi\')"} + active: "active(\'about\')", + select: "select(\'about\')"} %h3 About us %p= @group.long_description + %tab{heading: 'Our producers', active: "active(\'producers\')", select: "select(\'producers\')"} - %h3 Our producers - %ul - - @group.enterprises.is_primary_producer.each do |enterprise| - %li= link_to enterprise.name, enterprise + .producers.pad-top{"ng-controller" => "GroupEnterprisesCtrl"} + .row + .small-12.columns.pad-top + %h1 Our Producers + = render partial: "shared/components/enterprise_search" + -# TODO: find out why this is not working + -#= render partial: "producers/filters" + + .row{bindonce: true} + .small-12.columns + .active_table + %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", + "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)", + "ng-controller" => "GroupEnterpriseNodeCtrl", + "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", + id: "{{producer.hash}}"} + + .small-12.columns + = render partial: 'producers/skinny' + = render partial: 'producers/fat' + + = render partial: 'shared/components/enterprise_no_results' + %tab{heading: 'Our hubs', active: "active(\'hubs\')", select: "select(\'hubs\')"} - %h3 Our producers - %ul - - @group.enterprises.is_distributor.each do |enterprise| - %li= link_to enterprise.name, enterprise + #hubs.hubs{"ng-controller" => "GroupEnterprisesCtrl"} + .row + .small-12.columns + %h1 Our Hubs + + = render partial: "shared/components/enterprise_search" + -# TODO: find out why this is not working + -#= render partial: "home/filters" + + .row{bindonce: true} + .small-12.columns + .active_table + %hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}", + "ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", + "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", + "ng-controller" => "GroupEnterpriseNodeCtrl"} + .small-12.columns + = render partial: 'home/skinny' + = render partial: 'home/fat' + + = render partial: 'shared/components/enterprise_no_results' + %tab{heading: 'Contact us', active: "active(\'contact\')", select: "select(\'contact\')"} From 949808e839f25bc38fefd0bc8b4f125c520b28da Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 14 Jan 2015 18:03:08 +1100 Subject: [PATCH 009/384] Customising AdaptiveMenu for the admin tab panel The AdaptiveMenu was design for a menu filling the screen width. Our menu is in a skeleton structure. The new file overwrites the spree version and takes the container width as reference. --- .../assets/javascripts/jquery.adaptivemenu.js | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 vendor/assets/javascripts/jquery.adaptivemenu.js diff --git a/vendor/assets/javascripts/jquery.adaptivemenu.js b/vendor/assets/javascripts/jquery.adaptivemenu.js new file mode 100644 index 0000000000..8df8b98c65 --- /dev/null +++ b/vendor/assets/javascripts/jquery.adaptivemenu.js @@ -0,0 +1,62 @@ +/* + * Original from spree/core/vendor/assets/javascripts/jquery.adaptivemenu.js + */ + +/* + * Used for the spree admin tab bar (Orders, Products, Reports etc.). + * Using parent's width instead of window width. + */ +jQuery.fn.AdaptiveMenu = function(options){ + + var options = jQuery.extend({ + text: "More...", + accuracy:0, + 'class':null, + 'classLinckMore':null + },options); + + var menu = this; + var li = $(menu).find("li"); + + var width = 0; + var widthLi = []; + $.each( li , function(i, l){ + width += $(l).width(); + widthLi.push( width ); + }); + + var buildingMenu = function(){ + var windowWidth = $(menu.parent()).width() - options.accuracy; + for(var i = 0; i windowWidth ) + $( li[i] ).hide(); + else + $( li[i] ).show(); + } + $(menu).find('#more').remove(); + var hideLi = $(li).filter(':not(:visible)'); + var lastLi = $(li).filter(':visible').last(); + if ( hideLi.length > 0 ){ + var more = $("
  • ") + .css({"display":"inline-block","white-space":"nowrap"}) + .addClass(options.classLinckMore) + .attr({"id":"more"}) + .html(options.text) + .click(function(){$(this).find('li').toggle()}); + + var ul = $("
      ") + .css({"position":"absolute"}) + .addClass(options.klass) + .html(hideLi.clone()).prepend(lastLi.clone().hide()); + + more.append(ul); + + lastLi.hide().before(more); + } + } + + jQuery(window).resize(buildingMenu); + + jQuery(window).ready(buildingMenu); + +}; From 13cbbcef404e84fe18c15063d17bbc80b820796f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 15 Jan 2015 10:26:27 +1100 Subject: [PATCH 010/384] Admin Tab Menu: converting .rb to .html.haml.deface Plus a new menu item for enterprise groups. --- app/overrides/add_enterprises_admin_tab.rb | 5 ----- app/overrides/add_order_cycles_admin_tab.rb | 5 ----- .../layouts/admin/add_enterprises_admin_tab.html.haml.deface | 2 ++ .../layouts/admin/add_groups_admin_tab.html.haml.deface | 2 ++ .../admin/add_order_cycles_admin_tab.html.haml.deface | 2 ++ 5 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 app/overrides/add_enterprises_admin_tab.rb delete mode 100644 app/overrides/add_order_cycles_admin_tab.rb create mode 100644 app/overrides/spree/layouts/admin/add_enterprises_admin_tab.html.haml.deface create mode 100644 app/overrides/spree/layouts/admin/add_groups_admin_tab.html.haml.deface create mode 100644 app/overrides/spree/layouts/admin/add_order_cycles_admin_tab.html.haml.deface diff --git a/app/overrides/add_enterprises_admin_tab.rb b/app/overrides/add_enterprises_admin_tab.rb deleted file mode 100644 index 9fbb00f5f3..0000000000 --- a/app/overrides/add_enterprises_admin_tab.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/layouts/admin", - :name => "add_enterprises_admin_tab", - :insert_bottom => "[data-hook='admin_tabs'], #admin_tabs[data-hook]", - :text => "<%= tab :enterprises, :url => main_app.admin_enterprises_path %>", - :original => '6999548b86c700f2cc5d4f9d297c94b3617fd981') \ No newline at end of file diff --git a/app/overrides/add_order_cycles_admin_tab.rb b/app/overrides/add_order_cycles_admin_tab.rb deleted file mode 100644 index 6e36e413a8..0000000000 --- a/app/overrides/add_order_cycles_admin_tab.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/layouts/admin", - :name => "add_order_cycles_admin_tab", - :insert_bottom => "[data-hook='admin_tabs'], #admin_tabs[data-hook]", - :text => "<%= tab :order_cycles, :url => main_app.admin_order_cycles_path %>", - :original => 'd4e321201ecb543e92192a031c8896a45dde3576') \ No newline at end of file diff --git a/app/overrides/spree/layouts/admin/add_enterprises_admin_tab.html.haml.deface b/app/overrides/spree/layouts/admin/add_enterprises_admin_tab.html.haml.deface new file mode 100644 index 0000000000..cddadabed7 --- /dev/null +++ b/app/overrides/spree/layouts/admin/add_enterprises_admin_tab.html.haml.deface @@ -0,0 +1,2 @@ +/ insert_bottom "[data-hook='admin_tabs'], #admin_tabs[data-hook]" += tab :enterprises, :url => main_app.admin_enterprises_path diff --git a/app/overrides/spree/layouts/admin/add_groups_admin_tab.html.haml.deface b/app/overrides/spree/layouts/admin/add_groups_admin_tab.html.haml.deface new file mode 100644 index 0000000000..8ed9cf55fd --- /dev/null +++ b/app/overrides/spree/layouts/admin/add_groups_admin_tab.html.haml.deface @@ -0,0 +1,2 @@ +/ insert_bottom "[data-hook='admin_tabs'], #admin_tabs[data-hook]" += tab :groups, :url => main_app.admin_enterprise_groups_path diff --git a/app/overrides/spree/layouts/admin/add_order_cycles_admin_tab.html.haml.deface b/app/overrides/spree/layouts/admin/add_order_cycles_admin_tab.html.haml.deface new file mode 100644 index 0000000000..90b9007de5 --- /dev/null +++ b/app/overrides/spree/layouts/admin/add_order_cycles_admin_tab.html.haml.deface @@ -0,0 +1,2 @@ +/ insert_bottom "[data-hook='admin_tabs'], #admin_tabs[data-hook]" += tab :order_cycles, :url => main_app.admin_order_cycles_path From 3e5dfda324d82c1a8a769af62f1165734c5bdd41 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 15 Jan 2015 10:53:11 +1100 Subject: [PATCH 011/384] Removing old link to enterprise groups from configuration menu --- .../add_enterprise_groups_to_admin_configurations_menu.rb | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 app/overrides/add_enterprise_groups_to_admin_configurations_menu.rb diff --git a/app/overrides/add_enterprise_groups_to_admin_configurations_menu.rb b/app/overrides/add_enterprise_groups_to_admin_configurations_menu.rb deleted file mode 100644 index 9fb511fc8a..0000000000 --- a/app/overrides/add_enterprise_groups_to_admin_configurations_menu.rb +++ /dev/null @@ -1,6 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/admin/shared/_configuration_menu", - :name => "add_enterprise_groups_to_admin_configurations_menu", - :insert_bottom => "[data-hook='admin_configurations_sidebar_menu']", - :text => "
    • <%= link_to 'Enterprise Groups', main_app.admin_enterprise_groups_path %>
    • ", - :partial => 'enterprise_groups/admin_configurations_menu', - :original => '') From 4dc0701213d6eb88e77263423e575fb97c19b327 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 15 Jan 2015 15:04:21 +1100 Subject: [PATCH 012/384] Side menu for enterprise group page --- app/assets/javascripts/admin/all.js | 1 + .../enterprise_group_controller.js.coffee | 3 ++ .../side_menu_controller.js.coffee | 13 +++++ .../enterprise_groups.js.coffee | 1 + .../admin/enterprise_groups/_form.html.haml | 52 ++++--------------- .../admin/enterprise_groups/_inputs.html.haml | 52 +++++++++++++++++++ .../admin/enterprise_groups/edit.html.haml | 4 +- .../admin/enterprise_groups/new.html.haml | 4 +- .../admin/enterprises/_ng_form.html.haml | 2 +- .../_side_menu.html.haml | 0 10 files changed, 83 insertions(+), 49 deletions(-) create mode 100644 app/assets/javascripts/admin/enterprise_groups/controllers/enterprise_group_controller.js.coffee create mode 100644 app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee create mode 100644 app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee create mode 100644 app/views/admin/enterprise_groups/_inputs.html.haml rename app/views/admin/{enterprises => shared}/_side_menu.html.haml (100%) diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 78ff8306f8..9a07309352 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -19,6 +19,7 @@ //= require ../shared/ng-infinite-scroll.min.js //= require ./admin //= require ./enterprises/enterprises +//= require ./enterprise_groups/enterprise_groups //= require ./payment_methods/payment_methods //= require ./products/products //= require ./shipping_methods/shipping_methods diff --git a/app/assets/javascripts/admin/enterprise_groups/controllers/enterprise_group_controller.js.coffee b/app/assets/javascripts/admin/enterprise_groups/controllers/enterprise_group_controller.js.coffee new file mode 100644 index 0000000000..c207032853 --- /dev/null +++ b/app/assets/javascripts/admin/enterprise_groups/controllers/enterprise_group_controller.js.coffee @@ -0,0 +1,3 @@ +angular.module("admin.enterprise_groups") + .controller "enterpriseGroupCtrl", ($scope, SideMenu) -> + $scope.menu = SideMenu diff --git a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee new file mode 100644 index 0000000000..652f06e426 --- /dev/null +++ b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee @@ -0,0 +1,13 @@ +angular.module("admin.enterprise_groups") + .controller "sideMenuCtrl", ($scope, SideMenu) -> + $scope.menu = SideMenu + $scope.select = SideMenu.select + + $scope.menu.setItems [ + { name: 'Primary Details', icon_class: "icon-user" } + { name: 'About', icon_class: "icon-pencil" } + { name: 'Images', icon_class: "icon-picture" } + { name: 'Contact', icon_class: "icon-phone" } + ] + + $scope.select(0) diff --git a/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee b/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee new file mode 100644 index 0000000000..2d23b9c6fa --- /dev/null +++ b/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee @@ -0,0 +1 @@ +angular.module("admin.enterprise_groups", ["admin.side_menu"]) diff --git a/app/views/admin/enterprise_groups/_form.html.haml b/app/views/admin/enterprise_groups/_form.html.haml index ec4934bf19..9ebf6db147 100644 --- a/app/views/admin/enterprise_groups/_form.html.haml +++ b/app/views/admin/enterprise_groups/_form.html.haml @@ -1,43 +1,11 @@ -= f.field_container :name do - = f.label :name - %br/ - = f.text_field :name += render :partial => 'spree/shared/error_messages', :locals => { :target => @enterprise } -= f.field_container :description do - = f.label :description - %br/ - = f.text_field :description - -= f.field_container :long_description do - = f.label :long_description - %br/ - = f.text_area :long_description - -= f.field_container :on_front_page do - = f.label :on_front_page, 'On front page?' - %br/ - = f.check_box :on_front_page - -= f.field_container :enterprise_ids do - = f.label :enterprise_ids, 'Enterprises' - %br/ - = f.collection_select :enterprise_ids, Enterprise.all, :id, :name, {}, {class: "select2 fullwidth", multiple: true} - - -.row - .alpha.three.columns - = f.label :logo, class: 'with-tip', 'data-powertip' => 'This is the logo' - .with-tip{'data-powertip' => 'This is the logo'} - %a What's this? - .omega.eight.columns - = image_tag @object.logo.url if @object.logo.present? - = f.file_field :logo - -.row - .alpha.three.columns - = f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed at the top of the Group profile' - .with-tip{'data-powertip' => 'This image is displayed at the top of the Group profile'} - %a What's this? - .omega.eight.columns - = image_tag @object.promo_image.url if @object.promo_image.present? - = f.file_field :promo_image += form_for [main_app, :admin, @enterprise_group] do |f| + .row{ ng: {app: 'admin.enterprise_groups', controller: 'enterpriseGroupCtrl'} } + .sixteen.columns.alpha + .four.columns.alpha + = render 'admin/shared/side_menu' + .one.column   + .eleven.columns.omega.fullwidth_inputs + = render :partial => 'inputs', :locals => { :f => f } + = render partial: "spree/admin/shared/#{action}_resource_links" diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml new file mode 100644 index 0000000000..d3baec4a0c --- /dev/null +++ b/app/views/admin/enterprise_groups/_inputs.html.haml @@ -0,0 +1,52 @@ +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Primary Details'" } } + %legend Primary Details + = f.field_container :name do + = f.label :name + %br/ + = f.text_field :name + + = f.field_container :description do + = f.label :description + %br/ + = f.text_field :description + + = f.field_container :on_front_page do + = f.label :on_front_page, 'On front page?' + %br/ + = f.check_box :on_front_page + + = f.field_container :enterprise_ids do + = f.label :enterprise_ids, 'Enterprises' + %br/ + = f.collection_select :enterprise_ids, Enterprise.all, :id, :name, {}, {class: "select2 fullwidth", multiple: true} + +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='About'" } } + %legend About + = f.field_container :long_description do + = f.label :long_description + %br/ + = f.text_area :long_description + + +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Images'" } } + %legend Images + .row + .alpha.three.columns + = f.label :logo, class: 'with-tip', 'data-powertip' => 'This is the logo' + .with-tip{'data-powertip' => 'This is the logo'} + %a What's this? + .omega.eight.columns + = image_tag @object.logo.url if @object.logo.present? + = f.file_field :logo + .row + .alpha.three.columns + = f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed at the top of the Group profile' + .with-tip{'data-powertip' => 'This image is displayed at the top of the Group profile'} + %a What's this? + .omega.eight.columns + = image_tag @object.promo_image.url if @object.promo_image.present? + = f.file_field :promo_image + +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Contact'" } } + %legend Contact + TODO diff --git a/app/views/admin/enterprise_groups/edit.html.haml b/app/views/admin/enterprise_groups/edit.html.haml index 19c954f8e4..d83367b8c5 100644 --- a/app/views/admin/enterprise_groups/edit.html.haml +++ b/app/views/admin/enterprise_groups/edit.html.haml @@ -1,5 +1,3 @@ = render :partial => 'spree/shared/error_messages', :locals => { :target => @enterprise } -= form_for [main_app, :admin, @enterprise_group] do |f| - = render :partial => 'form', :locals => { :f => f } - = render :partial => 'spree/admin/shared/edit_resource_links' += render 'admin/enterprise_groups/form', action: 'edit' diff --git a/app/views/admin/enterprise_groups/new.html.haml b/app/views/admin/enterprise_groups/new.html.haml index f899eaa380..73d597a920 100644 --- a/app/views/admin/enterprise_groups/new.html.haml +++ b/app/views/admin/enterprise_groups/new.html.haml @@ -1,5 +1,3 @@ = render :partial => 'spree/shared/error_messages', :locals => { :target => @enterprise } -= form_for [main_app, :admin, @enterprise_group] do |f| - = render :partial => 'form', :locals => { :f => f } - = render :partial => 'spree/admin/shared/new_resource_links' += render 'admin/enterprise_groups/form', action: 'new' diff --git a/app/views/admin/enterprises/_ng_form.html.haml b/app/views/admin/enterprises/_ng_form.html.haml index 76ec768a3a..fa2d12aeae 100644 --- a/app/views/admin/enterprises/_ng_form.html.haml +++ b/app/views/admin/enterprises/_ng_form.html.haml @@ -12,7 +12,7 @@ .row .sixteen.columns.alpha .four.columns.alpha - = render 'side_menu' + = render 'admin/shared/side_menu' .one.column   .eleven.columns.omega.fullwidth_inputs = render 'form', f: f diff --git a/app/views/admin/enterprises/_side_menu.html.haml b/app/views/admin/shared/_side_menu.html.haml similarity index 100% rename from app/views/admin/enterprises/_side_menu.html.haml rename to app/views/admin/shared/_side_menu.html.haml From 71de15b3e4514fd4ffbb19a1a672a75a8710aafe Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 21 Jan 2015 12:32:07 +1100 Subject: [PATCH 013/384] Contact address for enterprise group --- .../admin/enterprise_groups_controller.rb | 16 ++++++++ app/models/enterprise_group.rb | 9 +++++ .../admin/enterprise_groups/_inputs.html.haml | 37 +++++++++++++++++-- ..._add_addresses_ref_to_enterprise_groups.rb | 6 +++ db/schema.rb | 7 +++- 5 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20150115050935_add_addresses_ref_to_enterprise_groups.rb diff --git a/app/controllers/admin/enterprise_groups_controller.rb b/app/controllers/admin/enterprise_groups_controller.rb index cc2f3ed1db..7a0763f5a4 100644 --- a/app/controllers/admin/enterprise_groups_controller.rb +++ b/app/controllers/admin/enterprise_groups_controller.rb @@ -1,5 +1,7 @@ module Admin class EnterpriseGroupsController < ResourceController + before_filter :load_countries, :except => :index + def index end @@ -15,8 +17,22 @@ module Admin redirect_to main_app.admin_enterprise_groups_path end + protected + + def build_resource_with_address + enterprise_group = build_resource_without_address + enterprise_group.address = Spree::Address.new + enterprise_group.address.country = Spree::Country.find_by_id(Spree::Config[:default_country_id]) + enterprise_group + end + alias_method_chain :build_resource, :address + private + def load_countries + @countries = Spree::Country.order(:name) + end + def collection EnterpriseGroup.by_position end diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 6e61b1a564..25fcadca5d 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -2,6 +2,10 @@ class EnterpriseGroup < ActiveRecord::Base acts_as_list has_and_belongs_to_many :enterprises + belongs_to :address, :class_name => 'Spree::Address' + accepts_nested_attributes_for :address + validates :address, presence: true, associated: true + before_validation :set_unused_address_fields validates :name, presence: true validates :description, presence: true @@ -28,4 +32,9 @@ class EnterpriseGroup < ActiveRecord::Base scope :by_position, order('position ASC') scope :on_front_page, where(on_front_page: true) + + def set_unused_address_fields + address.firstname = address.lastname = address.phone = 'unused' if address.present? + end + end diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml index d3baec4a0c..82fdc3eece 100644 --- a/app/views/admin/enterprise_groups/_inputs.html.haml +++ b/app/views/admin/enterprise_groups/_inputs.html.haml @@ -47,6 +47,37 @@ = image_tag @object.promo_image.url if @object.promo_image.present? = f.file_field :promo_image -%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Contact'" } } - %legend Contact - TODO += f.fields_for :address do |af| + %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Contact'" } } + %legend Contact + .row + .three.columns.alpha + = af.label :address1 + %span.required * + .eight.columns.omega + = af.text_field :address1, { placeholder: "eg. 123 High Street"} + .row + .alpha.three.columns + = af.label :address2 + .eight.columns.omega + = af.text_field :address2 + .row + .three.columns.alpha + = af.label :city, 'Suburb' + \/ + = af.label :zipcode, 'Postcode' + %span.required * + .four.columns + = af.text_field :city, { placeholder: "eg. Northcote"} + .four.columns.omega + = af.text_field :zipcode, { placeholder: "eg. 3070"} + .row + .three.columns.alpha + = af.label :state_id, 'State' + \/ + = af.label :country_id, 'Country' + %span.required * + .four.columns + = af.collection_select :state_id, af.object.country.states, :id, :name, {}, :class => "select2 fullwidth" + .four.columns.omega + = af.collection_select :country_id, available_countries, :id, :name, {}, :class => "select2 fullwidth" diff --git a/db/migrate/20150115050935_add_addresses_ref_to_enterprise_groups.rb b/db/migrate/20150115050935_add_addresses_ref_to_enterprise_groups.rb new file mode 100644 index 0000000000..8b2ca415b1 --- /dev/null +++ b/db/migrate/20150115050935_add_addresses_ref_to_enterprise_groups.rb @@ -0,0 +1,6 @@ +class AddAddressesRefToEnterpriseGroups < ActiveRecord::Migration + def change + add_column :enterprise_groups, :address_id, :integer + add_foreign_key :enterprise_groups, :spree_addresses, name: "enterprise_groups_address_id_fk", column: "address_id" + end +end diff --git a/db/schema.rb b/db/schema.rb index 3244916b3b..16c616f1a3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20141210233407) do +ActiveRecord::Schema.define(:version => 20150115050935) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -197,6 +197,7 @@ ActiveRecord::Schema.define(:version => 20141210233407) do t.string "logo_content_type" t.integer "logo_file_size" t.datetime "logo_updated_at" + t.integer "address_id" end create_table "enterprise_groups_enterprises", :id => false, :force => true do |t| @@ -573,9 +574,9 @@ ActiveRecord::Schema.define(:version => 20141210233407) do t.string "email" t.text "special_instructions" t.integer "distributor_id" - t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" + t.integer "order_cycle_id" t.integer "cart_id" end @@ -1077,6 +1078,8 @@ ActiveRecord::Schema.define(:version => 20141210233407) do add_foreign_key "enterprise_fees", "enterprises", name: "enterprise_fees_enterprise_id_fk" + add_foreign_key "enterprise_groups", "spree_addresses", name: "enterprise_groups_address_id_fk", column: "address_id" + 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" From 650e35c13e8497910b5618a118e711fc1dd24f5b Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 21 Jan 2015 15:34:31 +1100 Subject: [PATCH 014/384] Adding contact information to groups backend --- .../side_menu_controller.js.coffee | 1 + app/models/enterprise_group.rb | 3 ++ .../admin/enterprise_groups/_inputs.html.haml | 38 +++++++++++++++++++ ...627_add_web_conact_to_enterprise_groups.rb | 10 +++++ db/schema.rb | 8 +++- 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20150121030627_add_web_conact_to_enterprise_groups.rb diff --git a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee index 652f06e426..63f05a9376 100644 --- a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee @@ -8,6 +8,7 @@ angular.module("admin.enterprise_groups") { name: 'About', icon_class: "icon-pencil" } { name: 'Images', icon_class: "icon-picture" } { name: 'Contact', icon_class: "icon-phone" } + { name: 'Web', icon_class: "icon-globe" } ] $scope.select(0) diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 25fcadca5d..5b3b65e311 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -12,6 +12,9 @@ class EnterpriseGroup < ActiveRecord::Base attr_accessible :name, :description, :long_description, :on_front_page, :enterprise_ids attr_accessible :logo, :promo_image + attr_accessible :email, :website, :facebook, :instagram, :linkedin, :twitter + + delegate :phone, :to => :address has_attached_file :logo, styles: {medium: "100x100"}, diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml index 82fdc3eece..fabebdaef4 100644 --- a/app/views/admin/enterprise_groups/_inputs.html.haml +++ b/app/views/admin/enterprise_groups/_inputs.html.haml @@ -50,6 +50,11 @@ = f.fields_for :address do |af| %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Contact'" } } %legend Contact + .row + .alpha.three.columns + = f.label :phone + .omega.eight.columns + = f.text_field :phone, { placeholder: "eg. 98 7654 3210"} .row .three.columns.alpha = af.label :address1 @@ -81,3 +86,36 @@ = af.collection_select :state_id, af.object.country.states, :id, :name, {}, :class => "select2 fullwidth" .four.columns.omega = af.collection_select :country_id, available_countries, :id, :name, {}, :class => "select2 fullwidth" + +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Web'" } } + %legend Web Resources + .row + .alpha.three.columns + = f.label :website + .omega.eight.columns + = f.text_field :website, { placeholder: "eg. www.truffles.com"} + .row + .alpha.three.columns + = f.label :email + .omega.eight.columns + = f.text_field :email + .row + .alpha.three.columns + = f.label :facebook, 'Facebook' + .omega.eight.columns + = f.text_field :facebook + .row + .alpha.three.columns + = f.label :instagram, 'Instagram' + .omega.eight.columns + = f.text_field :instagram + .row + .alpha.three.columns + = f.label :linkedin, 'LinkedIn' + .omega.eight.columns + = f.text_field :linkedin + .row + .alpha.three.columns + = f.label :twitter + .omega.eight.columns + = f.text_field :twitter, { placeholder: "eg. @the_prof" } diff --git a/db/migrate/20150121030627_add_web_conact_to_enterprise_groups.rb b/db/migrate/20150121030627_add_web_conact_to_enterprise_groups.rb new file mode 100644 index 0000000000..1aca6a0e16 --- /dev/null +++ b/db/migrate/20150121030627_add_web_conact_to_enterprise_groups.rb @@ -0,0 +1,10 @@ +class AddWebConactToEnterpriseGroups < ActiveRecord::Migration + def change + add_column :enterprise_groups, :email, :string + add_column :enterprise_groups, :website, :string + add_column :enterprise_groups, :facebook, :string + add_column :enterprise_groups, :instagram, :string + add_column :enterprise_groups, :linkedin, :string + add_column :enterprise_groups, :twitter, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 16c616f1a3..418f5a7943 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150115050935) do +ActiveRecord::Schema.define(:version => 20150121030627) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -198,6 +198,12 @@ ActiveRecord::Schema.define(:version => 20150115050935) do t.integer "logo_file_size" t.datetime "logo_updated_at" t.integer "address_id" + t.string "email" + t.string "website" + t.string "facebook" + t.string "instagram" + t.string "linkedin" + t.string "twitter" end create_table "enterprise_groups_enterprises", :id => false, :force => true do |t| From c1aa2f9b3367863b697920135e305fdb940f0c12 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 21 Jan 2015 16:12:28 +1100 Subject: [PATCH 015/384] Display contact information in the front end --- app/views/groups/show.html.haml | 64 +++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 2ec509c0c5..cd50aa90a9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -79,8 +79,68 @@ %tab{heading: 'Contact us', active: "active(\'contact\')", select: "select(\'contact\')"} - %h3 Contact us - %p TODO: provide contact information + .row + .small-6.columns + %h3 Contact us + - if @group.phone + .row + .small-2.columns + Call + .small-10.columns + = @group.phone + - if @group.email + .row + .small-2.columns + Email + .small-10.columns + = @group.email + - if @group.website + .row + .small-2.columns + Website + .small-10.columns + = @group.website + .small-6.columns + %h3 Follow us + - if @group.facebook + .row + .small-2.columns + Facebook + .small-10.columns + = @group.facebook + - if @group.instagram + .row + .small-2.columns + Instagram + .small-10.columns + = @group.instagram + - if @group.linkedin + .row + .small-2.columns + LinkedIn + .small-10.columns + = @group.linkedin + - if @group.twitter + .row + .small-2.columns + Twitter + .small-10.columns + = @group.twitter + .row + .small-6.columns + %h3 Address + %p + = @group.address.address1 + - if @group.address.address2 + %br + = @group.address.address2 + %br + = @group.address.city + , + = @group.address.state + = @group.address.zipcode + %br + = @group.address.country %p TODO: show the group's contact footer From 0d9a0919e51eba086a5ff41c0146d596fc764ab3 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 21 Jan 2015 16:39:41 +1100 Subject: [PATCH 016/384] show contact a group's contact footer --- app/views/groups/show.html.haml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index cd50aa90a9..c354dd36a9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -142,6 +142,18 @@ %br = @group.address.country - %p TODO: show the group's contact footer + .text-center + %p + = @group.name + %p + -if @group.facebook + %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} + %i.ofn-i_044-facebook + -if @group.email + %a{title:'Email us', href: @group.email.reverse, mailto: true} + %i.ofn-i_050-mail-circle + -if @group.website + %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} + %i.ofn-i_049-web = render partial: "shared/footer" From 87b092fdf774f75bbcff44bb18fb7bf3adfcd91f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 22 Jan 2015 12:10:54 +1100 Subject: [PATCH 017/384] Adding addresses to existing groups and make them changable --- app/models/enterprise_group.rb | 3 ++- .../admin/enterprise_groups/_inputs.html.haml | 5 +++-- ...ress_instances_to_existing_enterprise_groups.rb | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 5b3b65e311..9a398e8c39 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -12,6 +12,7 @@ class EnterpriseGroup < ActiveRecord::Base attr_accessible :name, :description, :long_description, :on_front_page, :enterprise_ids attr_accessible :logo, :promo_image + attr_accessible :address_attributes attr_accessible :email, :website, :facebook, :instagram, :linkedin, :twitter delegate :phone, :to => :address @@ -37,7 +38,7 @@ class EnterpriseGroup < ActiveRecord::Base scope :on_front_page, where(on_front_page: true) def set_unused_address_fields - address.firstname = address.lastname = address.phone = 'unused' if address.present? + address.firstname = address.lastname = 'unused' if address.present? end end diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml index fabebdaef4..8b2db582a9 100644 --- a/app/views/admin/enterprise_groups/_inputs.html.haml +++ b/app/views/admin/enterprise_groups/_inputs.html.haml @@ -52,9 +52,10 @@ %legend Contact .row .alpha.three.columns - = f.label :phone + = af.label :phone + %span.required * .omega.eight.columns - = f.text_field :phone, { placeholder: "eg. 98 7654 3210"} + = af.text_field :phone, { placeholder: "eg. 98 7654 3210"} .row .three.columns.alpha = af.label :address1 diff --git a/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb b/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb new file mode 100644 index 0000000000..2978d286dc --- /dev/null +++ b/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb @@ -0,0 +1,14 @@ +class AddAddressInstancesToExistingEnterpriseGroups < ActiveRecord::Migration + def change + country = Spree::Country.find_by_name(ENV['DEFAULT_COUNTRY']) + state = country.states.first + EnterpriseGroup.all.each do |g| + if g.address.present? then + next + end + address = Spree::Address.new(firstname: 'unused', lastname: 'unused', address1: 'undefined', city: 'undefined', zipcode: 'undefined', state: state, country: country, phone: 'undefined') + g.address = address + g.save + end + end +end From 7a48d7fe2281b8138a990526f00fffdac1fde645 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 22 Jan 2015 16:04:46 +1100 Subject: [PATCH 018/384] Updating feature spec for groups --- spec/factories.rb | 1 + spec/features/admin/enterprise_groups_spec.rb | 28 ++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/spec/factories.rb b/spec/factories.rb index 214c61962d..d18d57ad22 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -136,6 +136,7 @@ FactoryGirl.define do name 'Enterprise group' description 'this is a group' on_front_page false + address { FactoryGirl.build(:address) } end sequence(:calculator_amount) diff --git a/spec/features/admin/enterprise_groups_spec.rb b/spec/features/admin/enterprise_groups_spec.rb index 24e89e9be7..59712f3bfe 100644 --- a/spec/features/admin/enterprise_groups_spec.rb +++ b/spec/features/admin/enterprise_groups_spec.rb @@ -15,28 +15,33 @@ feature %q{ e = create(:enterprise) group = create(:enterprise_group, enterprises: [e], on_front_page: true) - click_link 'Configuration' - click_link 'Enterprise Groups' + click_link 'Groups' page.should have_selector 'td', text: group.name page.should have_selector 'td', text: 'Y' page.should have_selector 'td', text: e.name end - scenario "creating a new enterprise group" do + scenario "creating a new enterprise group", js: true do e1 = create(:enterprise) e2 = create(:enterprise) e3 = create(:enterprise) - click_link 'Configuration' - click_link 'Enterprise Groups' + click_link 'Groups' click_link 'New Enterprise Group' fill_in 'enterprise_group_name', with: 'EGEGEG' fill_in 'enterprise_group_description', with: 'This is a description' check 'enterprise_group_on_front_page' - select e1.name, from: 'enterprise_group_enterprise_ids' - select e2.name, from: 'enterprise_group_enterprise_ids' + select2_search e1.name, from: 'Enterprises' + select2_search e2.name, from: 'Enterprises' + click_link 'Contact' + fill_in 'enterprise_group_address_attributes_phone', with: '000' + fill_in 'enterprise_group_address_attributes_address1', with: 'My Street' + fill_in 'enterprise_group_address_attributes_city', with: 'Block' + fill_in 'enterprise_group_address_attributes_zipcode', with: '0000' + select2_search 'Australia', :from => 'Country' + select2_search 'Victoria', :from => 'State' click_button 'Create' page.should have_content 'Enterprise group "EGEGEG" has been successfully created!' @@ -53,8 +58,7 @@ feature %q{ e2 = create(:enterprise) eg = create(:enterprise_group, name: 'EGEGEG', on_front_page: true, enterprises: [e1, e2]) - click_link 'Configuration' - click_link 'Enterprise Groups' + click_link 'Groups' first("a.edit-enterprise-group").click page.should have_field 'enterprise_group_name', with: 'EGEGEG' @@ -80,8 +84,7 @@ feature %q{ eg1 = create(:enterprise_group, name: 'A') eg2 = create(:enterprise_group, name: 'B') - click_link 'Configuration' - click_link 'Enterprise Groups' + click_link 'Groups' page.all('td.name').map(&:text).should == ['A', 'B'] all("a.move-down").first.click @@ -93,8 +96,7 @@ feature %q{ scenario "deleting an enterprise group", js: true do eg = create(:enterprise_group, name: 'EGEGEG') - click_link 'Configuration' - click_link 'Enterprise Groups' + click_link 'Groups' first("a.delete-resource").click page.should have_no_content 'EGEGEG' From d93a8b642870d0ba2273a0fe93fc226fcac893e7 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 22 Jan 2015 16:22:34 +1100 Subject: [PATCH 019/384] Enterprise and Group controllers use default country instead of default country id --- app/controllers/admin/enterprise_groups_controller.rb | 2 +- app/controllers/admin/enterprises_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/enterprise_groups_controller.rb b/app/controllers/admin/enterprise_groups_controller.rb index 7a0763f5a4..89d72262d5 100644 --- a/app/controllers/admin/enterprise_groups_controller.rb +++ b/app/controllers/admin/enterprise_groups_controller.rb @@ -22,7 +22,7 @@ module Admin def build_resource_with_address enterprise_group = build_resource_without_address enterprise_group.address = Spree::Address.new - enterprise_group.address.country = Spree::Country.find_by_id(Spree::Config[:default_country_id]) + enterprise_group.address.country = Spree::Country.find_by_name(ENV['DEFAULT_COUNTRY']) enterprise_group end alias_method_chain :build_resource, :address diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 0ee0541145..150c609d51 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -59,7 +59,7 @@ module Admin def build_resource_with_address enterprise = build_resource_without_address enterprise.address = Spree::Address.new - enterprise.address.country = Spree::Country.find_by_id(Spree::Config[:default_country_id]) + enterprise.address.country = Spree::Country.find_by_name(ENV['DEFAULT_COUNTRY']) enterprise end alias_method_chain :build_resource, :address From 8e42f29bdebba06dedeeb811df9bb9d91c8881e2 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 29 Jan 2015 15:52:20 +1100 Subject: [PATCH 020/384] WIP groups page styling and markup updates --- app/views/groups/show.html.haml | 329 +++++++++++++++++--------------- 1 file changed, 176 insertions(+), 153 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index c354dd36a9..64960581b9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,159 +1,182 @@ -%div{style: "padding: 1.5em;"} - %img{"src" => @group.promo_image, style: "display: block"} - %div{style: "margin-bottom: 1em"} - %img{"src" => @group.logo} - %div{style: "display: inline-block; vertical-align: middle"} - %h2{style: "margin-bottom: 0em"}= @group.name - %div= @group.description +.row.pad-top + .small-12.columns.pad-top + %header + .row + .small-12.columns + %img{"src" => @group.promo_image} + .row.pad-top + .small-3.medium-2.columns + %img{"src" => @group.logo} + .small-9.medium-10.columns + %h2= @group.name + %p= @group.description - #div{"ng-controller" => "TabsCtrl", style: "clear: both"} - %tabset - %tab{heading: 'Map', - active: "active(\'\')", - select: "select(\'\')"} - = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer - .map-container - %map{"ng-controller" => "MapCtrl", "ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} - %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} - %markers{models: "OfnMap.enterprises", fit: "true", - coords: "'self'", icon: "'icon'", click: "'reveal'"} - - %tab{heading: 'About us', - active: "active(\'about\')", - select: "select(\'about\')"} - %h3 About us - %p= @group.long_description - - %tab{heading: 'Our producers', - active: "active(\'producers\')", - select: "select(\'producers\')"} - .producers.pad-top{"ng-controller" => "GroupEnterprisesCtrl"} - .row - .small-12.columns.pad-top - %h1 Our Producers - = render partial: "shared/components/enterprise_search" - -# TODO: find out why this is not working - -#= render partial: "producers/filters" - - .row{bindonce: true} - .small-12.columns - .active_table - %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", - "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)", - "ng-controller" => "GroupEnterpriseNodeCtrl", - "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", - id: "{{producer.hash}}"} - - .small-12.columns - = render partial: 'producers/skinny' - = render partial: 'producers/fat' - - = render partial: 'shared/components/enterprise_no_results' - - %tab{heading: 'Our hubs', - active: "active(\'hubs\')", - select: "select(\'hubs\')"} - #hubs.hubs{"ng-controller" => "GroupEnterprisesCtrl"} - .row - .small-12.columns - %h1 Our Hubs - - = render partial: "shared/components/enterprise_search" - -# TODO: find out why this is not working - -#= render partial: "home/filters" - - .row{bindonce: true} - .small-12.columns - .active_table - %hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}", - "ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", - "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", - "ng-controller" => "GroupEnterpriseNodeCtrl"} - .small-12.columns - = render partial: 'home/skinny' - = render partial: 'home/fat' - - = render partial: 'shared/components/enterprise_no_results' - - %tab{heading: 'Contact us', - active: "active(\'contact\')", - select: "select(\'contact\')"} - .row - .small-6.columns - %h3 Contact us - - if @group.phone - .row - .small-2.columns - Call - .small-10.columns - = @group.phone - - if @group.email - .row - .small-2.columns - Email - .small-10.columns - = @group.email - - if @group.website - .row - .small-2.columns - Website - .small-10.columns - = @group.website - .small-6.columns - %h3 Follow us - - if @group.facebook - .row - .small-2.columns - Facebook - .small-10.columns - = @group.facebook - - if @group.instagram - .row - .small-2.columns - Instagram - .small-10.columns - = @group.instagram - - if @group.linkedin - .row - .small-2.columns - LinkedIn - .small-10.columns - = @group.linkedin - - if @group.twitter - .row - .small-2.columns - Twitter - .small-10.columns - = @group.twitter - .row - .small-6.columns - %h3 Address - %p - = @group.address.address1 - - if @group.address.address2 - %br - = @group.address.address2 - %br - = @group.address.city - , - = @group.address.state - = @group.address.zipcode - %br - = @group.address.country - - .text-center +.row.pad-top + .small-12.columns.pad-top %p - = @group.name + 123 %p - -if @group.facebook - %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} - %i.ofn-i_044-facebook - -if @group.email - %a{title:'Email us', href: @group.email.reverse, mailto: true} - %i.ofn-i_050-mail-circle - -if @group.website - %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} - %i.ofn-i_049-web + 456 + %p + 789 + +/ %div{style: "padding: 1.5em;"} +/ %img{"src" => @group.promo_image, style: "display: block"} +/ %div{style: "margin-bottom: 1em"} +/ %img{"src" => @group.logo} +/ %div{style: "display: inline-block; vertical-align: middle"} +/ %h2{style: "margin-bottom: 0em"}= @group.name +/ %div= @group.description + +/ #div{"ng-controller" => "TabsCtrl", style: "clear: both"} +/ %tabset + +/ %tab{heading: 'Map', +/ active: "active(\'\')", +/ select: "select(\'\')"} +/ = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer +/ .map-container +/ %map{"ng-controller" => "MapCtrl", "ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} +/ %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} +/ %markers{models: "OfnMap.enterprises", fit: "true", +/ coords: "'self'", icon: "'icon'", click: "'reveal'"} + +/ %tab{heading: 'About us', +/ active: "active(\'about\')", +/ select: "select(\'about\')"} +/ %h3 About us +/ %p= @group.long_description + +/ %tab{heading: 'Our producers', +/ active: "active(\'producers\')", +/ select: "select(\'producers\')"} +/ .producers.pad-top{"ng-controller" => "GroupEnterprisesCtrl"} +/ .row +/ .small-12.columns.pad-top +/ %h1 Our Producers +/ = render partial: "shared/components/enterprise_search" +/ -# TODO: find out why this is not working +/ -#= render partial: "producers/filters" + +/ .row{bindonce: true} +/ .small-12.columns +/ .active_table +/ %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", +/ "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)", +/ "ng-controller" => "GroupEnterpriseNodeCtrl", +/ "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", +/ id: "{{producer.hash}}"} + +/ .small-12.columns +/ = render partial: 'producers/skinny' +/ = render partial: 'producers/fat' + +/ = render partial: 'shared/components/enterprise_no_results' + +/ %tab{heading: 'Our hubs', +/ active: "active(\'hubs\')", +/ select: "select(\'hubs\')"} +/ #hubs.hubs{"ng-controller" => "GroupEnterprisesCtrl"} +/ .row +/ .small-12.columns +/ %h1 Our Hubs + +/ = render partial: "shared/components/enterprise_search" +/ -# TODO: find out why this is not working +/ -#= render partial: "home/filters" + +/ .row{bindonce: true} +/ .small-12.columns +/ .active_table +/ %hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}", +/ "ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", +/ "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", +/ "ng-controller" => "GroupEnterpriseNodeCtrl"} +/ .small-12.columns +/ = render partial: 'home/skinny' +/ = render partial: 'home/fat' + +/ = render partial: 'shared/components/enterprise_no_results' + +/ %tab{heading: 'Contact us', +/ active: "active(\'contact\')", +/ select: "select(\'contact\')"} +/ .row +/ .small-6.columns +/ %h3 Contact us +/ - if @group.phone +/ .row +/ .small-2.columns +/ Call +/ .small-10.columns +/ = @group.phone +/ - if @group.email +/ .row +/ .small-2.columns +/ Email +/ .small-10.columns +/ = @group.email +/ - if @group.website +/ .row +/ .small-2.columns +/ Website +/ .small-10.columns +/ = @group.website +/ .small-6.columns +/ %h3 Follow us +/ - if @group.facebook +/ .row +/ .small-2.columns +/ Facebook +/ .small-10.columns +/ = @group.facebook +/ - if @group.instagram +/ .row +/ .small-2.columns +/ Instagram +/ .small-10.columns +/ = @group.instagram +/ - if @group.linkedin +/ .row +/ .small-2.columns +/ LinkedIn +/ .small-10.columns +/ = @group.linkedin +/ - if @group.twitter +/ .row +/ .small-2.columns +/ Twitter +/ .small-10.columns +/ = @group.twitter +/ .row +/ .small-6.columns +/ %h3 Address +/ %p +/ = @group.address.address1 +/ - if @group.address.address2 +/ %br +/ = @group.address.address2 +/ %br +/ = @group.address.city +/ , +/ = @group.address.state +/ = @group.address.zipcode +/ %br +/ = @group.address.country + +/ .text-center +/ %p +/ = @group.name +/ %p +/ -if @group.facebook +/ %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} +/ %i.ofn-i_044-facebook +/ -if @group.email +/ %a{title:'Email us', href: @group.email.reverse, mailto: true} +/ %i.ofn-i_050-mail-circle +/ -if @group.website +/ %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} +/ %i.ofn-i_049-web = render partial: "shared/footer" From fcd9653a795939f52ee97536dfd20616a2e46c61 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 29 Jan 2015 15:52:40 +1100 Subject: [PATCH 021/384] Add padding to groups homepage --- app/views/groups/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index d9297d7202..09268230bc 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -3,7 +3,7 @@ :javascript angular.module('Darkswarm').value('groups', #{render partial: "json/groups", object: @groups}) -#groups{"ng-controller" => "GroupsCtrl"} +#groups.pad-top{"ng-controller" => "GroupsCtrl"} #active-table-search.row.pad-top .small-12.columns %h1 Groups / regions From fd363ff6c2e8733fb90e53ed87e4de8f72104ec1 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 29 Jan 2015 17:43:10 +1100 Subject: [PATCH 022/384] WIP Groups individual page styling --- app/assets/stylesheets/darkswarm/groups.css.sass | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 4686c452a2..91fc41fc9a 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -18,3 +18,12 @@ .ofn-i_035-groups font-size: 120% vertical-align: middle + +#group-page + @media screen and (min-width: 768px) + .group-logo + max-height: 100px + .group-name + border-bottom: 1px solid #ccc + .producers + background-image: none \ No newline at end of file From d99a54accfd14147c2e9e5f45862cd8f2a9eb6b0 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 29 Jan 2015 17:43:36 +1100 Subject: [PATCH 023/384] WIP groups individual page template markup - still needs alot of love. --- app/views/groups/show.html.haml | 143 ++++++++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 14 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 64960581b9..2ada84a9b4 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,25 +1,140 @@ -.row.pad-top +#group-page.row.pad-top .small-12.columns.pad-top %header .row .small-12.columns %img{"src" => @group.promo_image} - .row.pad-top - .small-3.medium-2.columns - %img{"src" => @group.logo} - .small-9.medium-10.columns - %h2= @group.name + .row + .small-12.medium-2.large-2.columns.pad-top + %img.group-logo{"src" => @group.logo} + .small-12.medium-10.large-10.columns.pad-top + %h2.group-name= @group.name %p= @group.description - -.row.pad-top .small-12.columns.pad-top - %p - 123 - %p - 456 - %p - 789 + .row.pad-top + .small-12.medium-7.columns + %div{"ng-controller" => "TabsCtrl"} + %tabset + %tab{heading: 'Map', + active: "active(\'\')", + select: "select(\'\')"} + = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer + .map-container + %map{"ng-controller" => "MapCtrl", "ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} + %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} + %markers{models: "OfnMap.enterprises", fit: "true", + coords: "'self'", icon: "'icon'", click: "'reveal'"} + + %tab{heading: 'About us', + active: "active(\'about\')", + select: "select(\'about\')"} + %h3.pad-top About us + %p= @group.long_description + + %tab{heading: 'Our producers', + active: "active(\'producers\')", + select: "select(\'producers\')"} + .producers.pad-top{"ng-controller" => "GroupEnterprisesCtrl"} + .row + .small-12.columns.pad-top + %h1 Our Producers + = render partial: "shared/components/enterprise_search" + -# TODO: find out why this is not working + -#= render partial: "producers/filters" + + .row{bindonce: true} + .small-12.columns + .active_table + %hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}", + "ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", + "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", + "ng-controller" => "GroupEnterpriseNodeCtrl"} + .small-12.columns + = render partial: 'home/skinny' + = render partial: 'home/fat' + + = render partial: 'shared/components/enterprise_no_results' + + .small-12.medium-4.medium-offset-1.columns + %h4 Contact us + - if @group.phone + .row + .small-2.columns + Call + .small-10.columns + = @group.phone + - if @group.email + .row + .small-2.columns + Email + .small-10.columns + = @group.email + - if @group.website + .row + .small-2.columns + Website + .small-10.columns + = @group.website + %p   + %h6 Address + %p + = @group.address.address1 + - if @group.address.address2 + %br + = @group.address.address2 + %br + = @group.address.city + , + = @group.address.state + = @group.address.zipcode + %br + = @group.address.country + %p + %h6 Follow us + - if @group.facebook + .row + .small-2.columns + Facebook + .small-10.columns + = @group.facebook + - if @group.instagram + .row + .small-2.columns + Instagram + .small-10.columns + = @group.instagram + - if @group.linkedin + .row + .small-2.columns + LinkedIn + .small-10.columns + = @group.linkedin + - if @group.twitter + .row + .small-2.columns + Twitter + .small-10.columns + = @group.twitter + + .small-12.columns.pad-top + .row.pad-top + .small-12.columns.text-center.small + %hr + Copyright this year + = @group.name + %p + -if @group.facebook + %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} + %i.ofn-i_044-facebook + -if @group.email + %a{title:'Email us', href: @group.email.reverse, mailto: true} + %i.ofn-i_050-mail-circle + -if @group.website + %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} + %i.ofn-i_049-web + %p +   / %div{style: "padding: 1.5em;"} / %img{"src" => @group.promo_image, style: "display: block"} From 5ea3733c8af6a88b6a231959fdc4b176a10157f6 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 30 Jan 2015 14:07:38 +1100 Subject: [PATCH 024/384] Groups page styling WIP --- app/views/groups/show.html.haml | 128 ++++++++++++++++---------------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 2ada84a9b4..5965860242 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -57,73 +57,75 @@ = render partial: 'shared/components/enterprise_no_results' .small-12.medium-4.medium-offset-1.columns - %h4 Contact us - - if @group.phone - .row - .small-2.columns - Call - .small-10.columns - = @group.phone - - if @group.email - .row - .small-2.columns - Email - .small-10.columns - = @group.email - - if @group.website - .row - .small-2.columns - Website - .small-10.columns - = @group.website - %p   - %h6 Address - %p - = @group.address.address1 - - if @group.address.address2 - %br - = @group.address.address2 - %br - = @group.address.city - , - = @group.address.state - = @group.address.zipcode - %br - = @group.address.country - %p - %h6 Follow us - - if @group.facebook - .row - .small-2.columns - Facebook - .small-10.columns - = @group.facebook - - if @group.instagram - .row - .small-2.columns - Instagram - .small-10.columns - = @group.instagram - - if @group.linkedin - .row - .small-2.columns - LinkedIn - .small-10.columns - = @group.linkedin - - if @group.twitter - .row - .small-2.columns - Twitter - .small-10.columns - = @group.twitter + %ng-include{src: "'partials/group-contact.html'"} + / %h4 Contact us + / - if @group.phone + / .row + / .small-2.columns + / Call + / .small-10.columns + / = @group.phone + / - if @group.email + / .row + / .small-2.columns + / Email + / .small-10.columns + / = @group.email + / - if @group.website + / .row + / .small-2.columns + / Website + / .small-10.columns + / = @group.website + / %p   + / %h6 Address + / %p + / = @group.address.address1 + / - if @group.address.address2 + / %br + / = @group.address.address2 + / %br + / = @group.address.city + / , + / = @group.address.state + / = @group.address.zipcode + / %br + / = @group.address.country + / %p + / %h6 Follow us + / - if @group.facebook + / .row + / .small-2.columns + / Facebook + / .small-10.columns + / = @group.facebook + / - if @group.instagram + / .row + / .small-2.columns + / Instagram + / .small-10.columns + / = @group.instagram + / - if @group.linkedin + / .row + / .small-2.columns + / LinkedIn + / .small-10.columns + / = @group.linkedin + / - if @group.twitter + / .row + / .small-2.columns + / Twitter + / .small-10.columns + / = @group.twitter .small-12.columns.pad-top .row.pad-top .small-12.columns.text-center.small %hr - Copyright this year - = @group.name - %p + %p.text-small + Copyright this year + = @group.name + %h2 -if @group.facebook %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} %i.ofn-i_044-facebook @@ -136,6 +138,8 @@ %p   +// ORIGINAL MIKAEL STUFF + / %div{style: "padding: 1.5em;"} / %img{"src" => @group.promo_image, style: "display: block"} / %div{style: "margin-bottom: 1em"} From 6fddb491db162bfdc0acbc3f212f3098a8625fbc Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 30 Jan 2015 14:07:55 +1100 Subject: [PATCH 025/384] New partial for groups contact --- .../partials/group-contact.html.haml | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 app/assets/javascripts/templates/partials/group-contact.html.haml diff --git a/app/assets/javascripts/templates/partials/group-contact.html.haml b/app/assets/javascripts/templates/partials/group-contact.html.haml new file mode 100644 index 0000000000..64774dce2e --- /dev/null +++ b/app/assets/javascripts/templates/partials/group-contact.html.haml @@ -0,0 +1,98 @@ +%div.contact-container{bindonce: true} + %p test 123 + + / - if "@group.email || @group.website || @group.phone" + %div.modal-centered + %p.modal-header Contact + %p + / = @group.phone + + + / %p.word-wrap{"ng-if" => "enterprise.email"} + / %a{"ng-href" => "{{enterprise.email | stripUrl}}", target: "_blank", mailto: true} + / %span.email + / {{ enterprise.email | stripUrl }} + + / %p.word-wrap{"ng-if" => "enterprise.website"} + / %a{"ng-href" => "http://{{enterprise.website | stripUrl}}", target: "_blank" } + / {{ enterprise.website | stripUrl }} + + + / %h4 Contact us + / - if @group.phone + / .row + / .small-2.columns + / Call + / .small-10.columns + / = @group.phone + / - if @group.email + / .row + / .small-2.columns + / Email + / .small-10.columns + / = @group.email + / - if @group.website + / .row + / .small-2.columns + / Website + / .small-10.columns + / = @group.website + / %p   + / %h6 Address + / %p + / = @group.address.address1 + / - if @group.address.address2 + / %br + / = @group.address.address2 + / %br + / = @group.address.city + / , + / = @group.address.state + / = @group.address.zipcode + / %br + / = @group.address.country + / %p + / %h6 Follow us + / - if @group.facebook + / .row + / .small-2.columns + / Facebook + / .small-10.columns + / = @group.facebook + / - if @group.instagram + / .row + / .small-2.columns + / Instagram + / .small-10.columns + / = @group.instagram + / - if @group.linkedin + / .row + / .small-2.columns + / LinkedIn + / .small-10.columns + / = @group.linkedin + / - if @group.twitter + / .row + / .small-2.columns + / Twitter + / .small-10.columns + / = @group.twitter + + / .small-12.columns.pad-top + / .row.pad-top + / .small-12.columns.text-center.small + / %hr + / Copyright this year + / = @group.name + / %p + / -if @group.facebook + / %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} + / %i.ofn-i_044-facebook + / -if @group.email + / %a{title:'Email us', href: @group.email.reverse, mailto: true} + / %i.ofn-i_050-mail-circle + / -if @group.website + / %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} + / %i.ofn-i_049-web + / %p + /   \ No newline at end of file From 8d9f8beff33051187ffc80068b5aa61923378939 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Mon, 2 Feb 2015 10:33:13 +1100 Subject: [PATCH 026/384] creating facebook url --- app/models/enterprise_group.rb | 16 ++++++++++++++++ spec/models/enterprise_group_spec.rb | 11 +++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 9a398e8c39..2b374c8687 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -41,4 +41,20 @@ class EnterpriseGroup < ActiveRecord::Base address.firstname = address.lastname = 'unused' if address.present? end + def facebook_url + if (facebook.blank?) then + return nil + end + if is_url? facebook then + facebook + else + 'https://www.facebook.com/' + facebook + end + end + + private + + def is_url?(s) + s.andand.include? '://' + end end diff --git a/spec/models/enterprise_group_spec.rb b/spec/models/enterprise_group_spec.rb index 3e38882064..4259c6009e 100644 --- a/spec/models/enterprise_group_spec.rb +++ b/spec/models/enterprise_group_spec.rb @@ -52,4 +52,15 @@ describe EnterpriseGroup do EnterpriseGroup.on_front_page.should == [eg1] end end + + describe "urls" do + it 'provides a Facebook URL' do + g = create(:enterprise_group) + g.facebook_url.should be_nil + g.facebook = 'test' + g.facebook_url.should == 'https://www.facebook.com/test' + g.facebook = 'http://fb.com/test' + g.facebook_url.should == g.facebook + end + end end From 91b35d068c19f846d11c63ca7a6db457fa7a4690 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Mon, 2 Feb 2015 15:58:34 +1100 Subject: [PATCH 027/384] Adding owner to groups Groups have owners and users own groups. The owners are displayed and changable on the group's page by admin users. --- .../enterprise_groups/enterprise_groups.js.coffee | 2 +- app/models/enterprise_group.rb | 2 ++ app/models/spree/user_decorator.rb | 1 + app/views/admin/enterprise_groups/_inputs.html.haml | 10 ++++++++++ app/views/admin/enterprise_groups/index.html.haml | 4 ++++ .../20150202000203_add_owner_to_enterprise_groups.rb | 6 ++++++ db/schema.rb | 4 +++- spec/models/spree/user_spec.rb | 12 ++++++++++++ 8 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20150202000203_add_owner_to_enterprise_groups.rb diff --git a/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee b/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee index 2d23b9c6fa..e8e462d998 100644 --- a/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee +++ b/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee @@ -1 +1 @@ -angular.module("admin.enterprise_groups", ["admin.side_menu"]) +angular.module("admin.enterprise_groups", ["admin.side_menu", "admin.users"]) diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 2b374c8687..4c9e97adb9 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -2,6 +2,7 @@ class EnterpriseGroup < ActiveRecord::Base acts_as_list has_and_belongs_to_many :enterprises + belongs_to :owner, class_name: 'Spree::User', foreign_key: :owner_id, inverse_of: :owned_groups belongs_to :address, :class_name => 'Spree::Address' accepts_nested_attributes_for :address validates :address, presence: true, associated: true @@ -11,6 +12,7 @@ class EnterpriseGroup < ActiveRecord::Base validates :description, presence: true attr_accessible :name, :description, :long_description, :on_front_page, :enterprise_ids + attr_accessible :owner_id attr_accessible :logo, :promo_image attr_accessible :address_attributes attr_accessible :email, :website, :facebook, :instagram, :linkedin, :twitter diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 4f119ab81b..58c962985b 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -2,6 +2,7 @@ Spree.user_class.class_eval do has_many :enterprise_roles, :dependent => :destroy has_many :enterprises, through: :enterprise_roles has_many :owned_enterprises, class_name: 'Enterprise', foreign_key: :owner_id, inverse_of: :owner + has_many :owned_groups, class_name: 'EnterpriseGroup', foreign_key: :owner_id, inverse_of: :owner has_one :cart accepts_nested_attributes_for :enterprise_roles, :allow_destroy => true diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml index 8b2db582a9..60181e09c4 100644 --- a/app/views/admin/enterprise_groups/_inputs.html.haml +++ b/app/views/admin/enterprise_groups/_inputs.html.haml @@ -10,6 +10,16 @@ %br/ = f.text_field :description + - if spree_current_user.admin? + .row + .three.columns.alpha + =f.label :owner_id, 'Owner' + .with-tip{'data-powertip' => "The primary user responsible for this group."} + %a What's this? + .eight.columns.omega + - owner_email = @enterprise_group.andand.owner.andand.email || "" + = f.hidden_field :owner_id, class: "select2 fullwidth", 'ofn-user-autocomplete' => true, email: owner_email + = f.field_container :on_front_page do = f.label :on_front_page, 'On front page?' %br/ diff --git a/app/views/admin/enterprise_groups/index.html.haml b/app/views/admin/enterprise_groups/index.html.haml index 035402267f..3bbf21db6e 100644 --- a/app/views/admin/enterprise_groups/index.html.haml +++ b/app/views/admin/enterprise_groups/index.html.haml @@ -9,6 +9,8 @@ %thead %tr %th Name + - if spree_current_user.admin? + %th Owner %th On front page? %th Enterprises %th.actions @@ -17,6 +19,8 @@ - @enterprise_groups.each do |enterprise_group| %tr %td.name= enterprise_group.name + - if spree_current_user.admin? + %td= enterprise_group.owner.andand.email || "" %td= enterprise_group.on_front_page ? 'Y' : 'N' %td= enterprise_group.enterprises.map(&:name).join ', ' %td.actions diff --git a/db/migrate/20150202000203_add_owner_to_enterprise_groups.rb b/db/migrate/20150202000203_add_owner_to_enterprise_groups.rb new file mode 100644 index 0000000000..82ffdf9f5d --- /dev/null +++ b/db/migrate/20150202000203_add_owner_to_enterprise_groups.rb @@ -0,0 +1,6 @@ +class AddOwnerToEnterpriseGroups < ActiveRecord::Migration + def change + add_column :enterprise_groups, :owner_id, :integer + add_foreign_key :enterprise_groups, :spree_users, name: "enterprise_groups_owner_id_fk", column: "owner_id" + end +end diff --git a/db/schema.rb b/db/schema.rb index 418f5a7943..c92989be2f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150121030627) do +ActiveRecord::Schema.define(:version => 20150202000203) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -204,6 +204,7 @@ ActiveRecord::Schema.define(:version => 20150121030627) do t.string "instagram" t.string "linkedin" t.string "twitter" + t.integer "owner_id" end create_table "enterprise_groups_enterprises", :id => false, :force => true do |t| @@ -1085,6 +1086,7 @@ ActiveRecord::Schema.define(:version => 20150121030627) do add_foreign_key "enterprise_fees", "enterprises", name: "enterprise_fees_enterprise_id_fk" add_foreign_key "enterprise_groups", "spree_addresses", name: "enterprise_groups_address_id_fk", column: "address_id" + add_foreign_key "enterprise_groups", "spree_users", name: "enterprise_groups_owner_id_fk", column: "owner_id" 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" diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index e1a9239c95..46fb0d924b 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -22,6 +22,18 @@ describe Spree.user_class do }.to raise_error ActiveRecord::RecordInvalid, "Validation failed: #{u2.email} is not permitted to own any more enterprises (limit is 1)." end end + + describe "group ownership" do + let(:u1) { create(:user) } + let(:u2) { create(:user) } + let!(:g1) { create(:enterprise_group, owner: u1) } + let!(:g2) { create(:enterprise_group, owner: u1) } + + it "provides access to owned groups" do + expect(u1.owned_groups(:reload)).to include g1, g2 + end + + end end context "#create" do From a7c2a73fa802245eea7f31013a9adfad78765611 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 5 Feb 2015 12:05:39 +1100 Subject: [PATCH 028/384] make groups editable by group owners --- .../admin/enterprise_groups_controller.rb | 1 + app/models/enterprise_group.rb | 7 +++++++ app/models/spree/ability_decorator.rb | 14 ++++++++++++++ .../admin/add_groups_admin_tab.html.haml.deface | 2 +- app/views/admin/enterprise_groups/index.html.haml | 12 +++++++----- spec/models/enterprise_group_spec.rb | 9 +++++++++ 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/app/controllers/admin/enterprise_groups_controller.rb b/app/controllers/admin/enterprise_groups_controller.rb index 89d72262d5..3968cc5289 100644 --- a/app/controllers/admin/enterprise_groups_controller.rb +++ b/app/controllers/admin/enterprise_groups_controller.rb @@ -3,6 +3,7 @@ module Admin before_filter :load_countries, :except => :index def index + @enterprise_groups = @enterprise_groups.managed_by(spree_current_user) end def move_up diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 4c9e97adb9..28a92b81f5 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -38,6 +38,13 @@ class EnterpriseGroup < ActiveRecord::Base scope :by_position, order('position ASC') scope :on_front_page, where(on_front_page: true) + scope :managed_by, lambda { |user| + if user.has_spree_role?('admin') + scoped + else + where('owner_id = ?', user.id); + end + } def set_unused_address_fields address.firstname = address.lastname = 'unused' if address.present? diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 23138fcea9..336a46b9b5 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -6,6 +6,7 @@ class AbilityDecorator def initialize(user) add_base_abilities user if is_new_user? user add_enterprise_management_abilities user if can_manage_enterprises? user + add_group_management_abilities user if can_manage_groups? 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 @@ -21,6 +22,11 @@ class AbilityDecorator user.enterprises.present? end + # Users can manage a group if they have one. + def can_manage_groups?(user) + user.owned_groups.present? + end + # Users can manage products if they have an enterprise that is not a profile. def can_manage_products?(user) can_manage_enterprises?(user) && @@ -41,6 +47,14 @@ class AbilityDecorator can [:create], Enterprise end + def add_group_management_abilities(user) + can [:admin, :index], :overview + can [:admin, :index], EnterpriseGroup + can [:read, :edit, :update], EnterpriseGroup do |group| + user.owned_groups.include? group + end + end + def add_enterprise_management_abilities(user) # Spree performs authorize! on (:create, nil) when creating a new order from admin, and also (:search, nil) # when searching for variants to add to the order diff --git a/app/overrides/spree/layouts/admin/add_groups_admin_tab.html.haml.deface b/app/overrides/spree/layouts/admin/add_groups_admin_tab.html.haml.deface index 8ed9cf55fd..fe303a453c 100644 --- a/app/overrides/spree/layouts/admin/add_groups_admin_tab.html.haml.deface +++ b/app/overrides/spree/layouts/admin/add_groups_admin_tab.html.haml.deface @@ -1,2 +1,2 @@ / insert_bottom "[data-hook='admin_tabs'], #admin_tabs[data-hook]" -= tab :groups, :url => main_app.admin_enterprise_groups_path += tab :enterprise_groups, :url => main_app.admin_enterprise_groups_path, label: 'groups' diff --git a/app/views/admin/enterprise_groups/index.html.haml b/app/views/admin/enterprise_groups/index.html.haml index 3bbf21db6e..3ccf82c10c 100644 --- a/app/views/admin/enterprise_groups/index.html.haml +++ b/app/views/admin/enterprise_groups/index.html.haml @@ -27,8 +27,10 @@ = link_to '', main_app.edit_admin_enterprise_group_path(enterprise_group), class: 'edit-enterprise-group icon-edit no-text' = link_to_delete enterprise_group, no_text: true - - if enterprise_group.last? - .blank-action - - else - = link_to_with_icon 'icon-arrow-down', '', main_app.admin_enterprise_group_move_down_path(enterprise_group), class: 'move-down no-text' - = link_to_with_icon 'icon-arrow-up', '', main_app.admin_enterprise_group_move_up_path(enterprise_group), class: 'move-up no-text' unless enterprise_group.first? + - if spree_current_user.admin? + - if enterprise_group.last? + .blank-action + - else + = link_to_with_icon 'icon-arrow-down', '', main_app.admin_enterprise_group_move_down_path(enterprise_group), class: 'move-down no-text' + - if enterprise_group.first? + = link_to_with_icon 'icon-arrow-up', '', main_app.admin_enterprise_group_move_up_path(enterprise_group), class: 'move-up no-text' diff --git a/spec/models/enterprise_group_spec.rb b/spec/models/enterprise_group_spec.rb index 4259c6009e..9642b146c7 100644 --- a/spec/models/enterprise_group_spec.rb +++ b/spec/models/enterprise_group_spec.rb @@ -51,6 +51,15 @@ describe EnterpriseGroup do EnterpriseGroup.on_front_page.should == [eg1] end + + it "finds a user's enterprise groups" do + user = create(:user) + user.spree_roles = [] + eg1 = create(:enterprise_group, owner: user) + eg2 = create(:enterprise_group) + + EnterpriseGroup.managed_by(user).should == [eg1] + end end describe "urls" do From b1b1aa5b1e9113d79cf21b9b749ebbb810333617 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Feb 2015 12:07:11 +1100 Subject: [PATCH 029/384] Groups page WIP with mikael and rob --- .../partials/group-contact.html.haml | 24 +++++++++++++++---- .../stylesheets/darkswarm/branding.css.sass | 1 + .../stylesheets/darkswarm/groups.css.sass | 20 +++++++++++++++- app/views/groups/show.html.haml | 7 +++--- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/templates/partials/group-contact.html.haml b/app/assets/javascripts/templates/partials/group-contact.html.haml index 64774dce2e..ea56fe244a 100644 --- a/app/assets/javascripts/templates/partials/group-contact.html.haml +++ b/app/assets/javascripts/templates/partials/group-contact.html.haml @@ -1,12 +1,26 @@ -%div.contact-container{bindonce: true} - %p test 123 - - / - if "@group.email || @group.website || @group.phone" +%div.contact-container{bindonce: true} + / - if @group.email || @group.website || @group.phone %div.modal-centered %p.modal-header Contact - %p + %p + Container for contact info / = @group.phone +%div.contact-container{bindonce: true} + / - if @group.email || @group.website || @group.phone + %div.modal-centered + %p.modal-header Address + %p + Container for address info + / = @group.phone + +%div.contact-container{bindonce: true} + / - if @group.email || @group.website || @group.phone + %div.modal-centered + %p.modal-header Follow + %p + Container for follow us info + / = @group.phone / %p.word-wrap{"ng-if" => "enterprise.email"} / %a{"ng-href" => "{{enterprise.email | stripUrl}}", target: "_blank", mailto: true} diff --git a/app/assets/stylesheets/darkswarm/branding.css.sass b/app/assets/stylesheets/darkswarm/branding.css.sass index 0fa559240f..ceca03abbf 100644 --- a/app/assets/stylesheets/darkswarm/branding.css.sass +++ b/app/assets/stylesheets/darkswarm/branding.css.sass @@ -22,4 +22,5 @@ $disabled-dark: #999 $disabled-v-dark: #808080 $med-grey: #666 $dark-grey: #333 +$light-grey: #ddd $black: #000 diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 91fc41fc9a..d5f865c38a 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -26,4 +26,22 @@ .group-name border-bottom: 1px solid #ccc .producers - background-image: none \ No newline at end of file + background-image: none + .tabs dd a + border: none + // border-bottom: 1px solid grey + margin-bottom: -2px + margin-right: 2px + .tabs dd.active a + margin-bottom: -1px + border-top: 1px solid $light-grey + border-left: 1px solid $light-grey + border-right: 1px solid $light-grey + border-bottom: 1px solid white + + .tabs-content + border-top: 1px solid $light-grey + border-left: 1px solid $light-grey + border-right: 1px solid $light-grey + border-bottom: 1px solid $light-grey + padding: 1.5em \ No newline at end of file diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 5965860242..5f8fe03ddc 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -13,7 +13,7 @@ .small-12.columns.pad-top .row.pad-top - .small-12.medium-7.columns + .small-12.medium-8.columns %div{"ng-controller" => "TabsCtrl"} %tabset %tab{heading: 'Map', @@ -56,7 +56,7 @@ = render partial: 'shared/components/enterprise_no_results' - .small-12.medium-4.medium-offset-1.columns + .small-12.medium-4.columns %ng-include{src: "'partials/group-contact.html'"} / %h4 Contact us / - if @group.phone @@ -123,8 +123,7 @@ .small-12.columns.text-center.small %hr %p.text-small - Copyright this year - = @group.name + = "Copyright #{Date.today.year} #{@group.name}" %h2 -if @group.facebook %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} From 0f7b8804090baf31fd074cd3b1f89db6033f80aa Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 5 Feb 2015 12:23:08 +1100 Subject: [PATCH 030/384] restoring groups hubs tab --- app/views/groups/show.html.haml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 5f8fe03ddc..09e0eff0b9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -43,6 +43,33 @@ -# TODO: find out why this is not working -#= render partial: "producers/filters" + .row{bindonce: true} + .small-12.columns + .active_table + %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", + "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)", + "ng-controller" => "GroupEnterpriseNodeCtrl", + "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", + id: "{{producer.hash}}"} + + .small-12.columns + = render partial: 'producers/skinny' + = render partial: 'producers/fat' + + = render partial: 'shared/components/enterprise_no_results' + + %tab{heading: 'Our hubs', + active: "active(\'hubs\')", + select: "select(\'hubs\')"} + #hubs.hubs{"ng-controller" => "GroupEnterprisesCtrl"} + .row + .small-12.columns + %h1 Our Hubs + + = render partial: "shared/components/enterprise_search" + -# TODO: find out why this is not working + -#= render partial: "home/filters" + .row{bindonce: true} .small-12.columns .active_table From c9bbe80738b36d82c9ded91ea4d3603f3c736ddd Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Feb 2015 12:28:24 +1100 Subject: [PATCH 031/384] Remove padding class to producers tab content --- app/views/groups/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 09e0eff0b9..1fd264c78a 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -35,7 +35,7 @@ %tab{heading: 'Our producers', active: "active(\'producers\')", select: "select(\'producers\')"} - .producers.pad-top{"ng-controller" => "GroupEnterprisesCtrl"} + .producers{"ng-controller" => "GroupEnterprisesCtrl"} .row .small-12.columns.pad-top %h1 Our Producers From 28b2dd40dd7ff082ca937f9333d4e5e43d582e66 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Feb 2015 12:31:38 +1100 Subject: [PATCH 032/384] Kill the pad top for Producers tab content too --- app/views/groups/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 1fd264c78a..c553e063dd 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -37,7 +37,7 @@ select: "select(\'producers\')"} .producers{"ng-controller" => "GroupEnterprisesCtrl"} .row - .small-12.columns.pad-top + .small-12.columns %h1 Our Producers = render partial: "shared/components/enterprise_search" -# TODO: find out why this is not working From e19aaf6be8ebceced899a68f66eeb64edf27dda0 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Feb 2015 14:53:09 +1100 Subject: [PATCH 033/384] Styling tabs for responsive design, improving groups page layout --- .../stylesheets/darkswarm/groups.css.sass | 33 ++++++++++++++++--- app/views/groups/show.html.haml | 6 ++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index d5f865c38a..1cdfcc8f8a 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -25,23 +25,46 @@ max-height: 100px .group-name border-bottom: 1px solid #ccc - .producers - background-image: none + + // Tabs .tabs dd a + padding: 0.35rem 0.5rem + font-size: 0.75rem border: none - // border-bottom: 1px solid grey margin-bottom: -2px margin-right: 2px + @media screen and (min-width: 400px) + .tabs dd a + padding: 0.45rem 0.75rem + font-size: 0.875rem + @media screen and (min-width: 768px) + .tabs dd a + padding: 1rem 2rem + font-size: 1rem .tabs dd.active a margin-bottom: -1px border-top: 1px solid $light-grey border-left: 1px solid $light-grey border-right: 1px solid $light-grey border-bottom: 1px solid white - .tabs-content border-top: 1px solid $light-grey border-left: 1px solid $light-grey border-right: 1px solid $light-grey border-bottom: 1px solid $light-grey - padding: 1.5em \ No newline at end of file + padding: 1.5em + + // Producers tab + .producers + background-image: none + .active_table .active_table_node a.is_distributor, .active_table .active_table_node a.is_distributor i.ofn-i_059-producer + color: $clr-turquoise + // Hubs tab + .hubs + background-image: none + padding-top: 0 + padding-bottom: 0 + +// .hubs .active_table .active_table_node.inactive.closed a, .hubs .active_table .active_table_node.inactive.closed a *, .hubs .active_table .active_table_node.inactive.open a, .hubs .active_table .active_table_node.inactive.open a * + + \ No newline at end of file diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index c553e063dd..c6f262f4c9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -13,7 +13,7 @@ .small-12.columns.pad-top .row.pad-top - .small-12.medium-8.columns + .small-12.large-8.columns %div{"ng-controller" => "TabsCtrl"} %tabset %tab{heading: 'Map', @@ -61,7 +61,7 @@ %tab{heading: 'Our hubs', active: "active(\'hubs\')", select: "select(\'hubs\')"} - #hubs.hubs{"ng-controller" => "GroupEnterprisesCtrl"} + .hubs{"ng-controller" => "GroupEnterprisesCtrl"} .row .small-12.columns %h1 Our Hubs @@ -83,7 +83,7 @@ = render partial: 'shared/components/enterprise_no_results' - .small-12.medium-4.columns + .small-12.large-4.columns %ng-include{src: "'partials/group-contact.html'"} / %h4 Contact us / - if @group.phone From f2e3d298fab6ba7c3e366ae992225d4404a5b5b2 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Feb 2015 15:17:50 +1100 Subject: [PATCH 034/384] More styling for responsive sizes groups header --- app/assets/stylesheets/darkswarm/groups.css.sass | 10 +++++++--- app/views/groups/show.html.haml | 6 +++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 1cdfcc8f8a..17dcedeb34 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -20,11 +20,15 @@ vertical-align: middle #group-page - @media screen and (min-width: 768px) - .group-logo - max-height: 100px + .group-logo, .group-header + text-align: center .group-name border-bottom: 1px solid #ccc + @media screen and (min-width: 768px) + .group-logo, .group-header + text-align: left + .group-logo img + max-height: 100px // Tabs .tabs dd a diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index c6f262f4c9..151f0419f7 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -5,9 +5,9 @@ .small-12.columns %img{"src" => @group.promo_image} .row - .small-12.medium-2.large-2.columns.pad-top - %img.group-logo{"src" => @group.logo} - .small-12.medium-10.large-10.columns.pad-top + .small-12.medium-2.large-2.columns.group-logo.pad-top + %img{"src" => @group.logo} + .small-12.medium-10.large-10.columns.group-header.pad-top %h2.group-name= @group.name %p= @group.description From 1b51ea0e3a3aef266e2496320bc19ae588b00c4b Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Feb 2015 15:42:24 +1100 Subject: [PATCH 035/384] Groups logo default image --- app/assets/images/noimage/group.png | Bin 0 -> 17189 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/assets/images/noimage/group.png diff --git a/app/assets/images/noimage/group.png b/app/assets/images/noimage/group.png new file mode 100644 index 0000000000000000000000000000000000000000..fe402076c004a8349900db9660ab1371e4fe8663 GIT binary patch literal 17189 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 Date: Thu, 5 Feb 2015 15:50:33 +1100 Subject: [PATCH 036/384] moving contact partial and giving example code --- .../groups/_contact.html.haml} | 26 ++++++++++++++----- app/views/groups/show.html.haml | 2 +- 2 files changed, 20 insertions(+), 8 deletions(-) rename app/{assets/javascripts/templates/partials/group-contact.html.haml => views/groups/_contact.html.haml} (83%) diff --git a/app/assets/javascripts/templates/partials/group-contact.html.haml b/app/views/groups/_contact.html.haml similarity index 83% rename from app/assets/javascripts/templates/partials/group-contact.html.haml rename to app/views/groups/_contact.html.haml index ea56fe244a..8dd8c7724e 100644 --- a/app/assets/javascripts/templates/partials/group-contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -1,10 +1,22 @@ %div.contact-container{bindonce: true} - / - if @group.email || @group.website || @group.phone - %div.modal-centered - %p.modal-header Contact - %p - Container for contact info - / = @group.phone + + - if @group.email.present? || @group.website.present? || @group.phone.present? + %div.modal-centered + %p.modal-header Contact + %p + - if @group.phone.present? && @group.phone != 'undefined' + Call + %a{tel: @group.phone} + = @group.phone + - if @group.email.present? + Email + %a{mailto: true, href: @group.email.reverse} + = @group.email.gsub('@', ' at ').gsub('.', ' dot ') + - if @group.website.present? + Website + %a{href: @group.website} + = @group.website + %div.contact-container{bindonce: true} / - if @group.email || @group.website || @group.phone @@ -109,4 +121,4 @@ / %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} / %i.ofn-i_049-web / %p - /   \ No newline at end of file + /   diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 09e0eff0b9..0d5b8ed0b9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -84,7 +84,7 @@ = render partial: 'shared/components/enterprise_no_results' .small-12.medium-4.columns - %ng-include{src: "'partials/group-contact.html'"} + = render partial: 'contact' / %h4 Contact us / - if @group.phone / .row From 37dbd376e90f190401b585553cb29cd43126dec0 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 5 Feb 2015 15:54:53 +1100 Subject: [PATCH 037/384] fixing indent --- app/views/groups/_contact.html.haml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index 8dd8c7724e..b4dcda8b04 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -5,17 +5,17 @@ %p.modal-header Contact %p - if @group.phone.present? && @group.phone != 'undefined' - Call - %a{tel: @group.phone} - = @group.phone + Call + %a{tel: @group.phone} + = @group.phone - if @group.email.present? - Email - %a{mailto: true, href: @group.email.reverse} - = @group.email.gsub('@', ' at ').gsub('.', ' dot ') + Email + %a{mailto: true, href: @group.email.reverse} + = @group.email.gsub('@', ' at ').gsub('.', ' dot ') - if @group.website.present? - Website - %a{href: @group.website} - = @group.website + Website + %a{href: @group.website} + = @group.website %div.contact-container{bindonce: true} From 242fb49276924b51c22231a40ce965e86593fd71 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 5 Feb 2015 16:17:29 +1100 Subject: [PATCH 038/384] checking for undefined phone number in model --- app/models/enterprise_group.rb | 4 ++++ app/views/groups/_contact.html.haml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 28a92b81f5..ccbd79f198 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -50,6 +50,10 @@ class EnterpriseGroup < ActiveRecord::Base address.firstname = address.lastname = 'unused' if address.present? end + def phone + address.phone.andand.sub('undefined', '') + end + def facebook_url if (facebook.blank?) then return nil diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index b4dcda8b04..7f7937edd4 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -4,7 +4,7 @@ %div.modal-centered %p.modal-header Contact %p - - if @group.phone.present? && @group.phone != 'undefined' + - if @group.phone.present? Call %a{tel: @group.phone} = @group.phone From 148333467f9de3e275dc140d738a1315587da6f4 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Feb 2015 17:06:32 +1100 Subject: [PATCH 039/384] Groups styling and markup - finishing up contact info partial etc --- .../stylesheets/darkswarm/groups.css.sass | 7 +- app/views/groups/_contact.html.haml | 163 ++++++------------ app/views/groups/index.html.haml | 6 +- 3 files changed, 58 insertions(+), 118 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 17dcedeb34..0c5aa8483a 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -1,6 +1,7 @@ @import branding @import mixins +// Search page #groups background-color: $clr-brick-light background-image: url("/assets/groups.svg") @@ -18,7 +19,8 @@ .ofn-i_035-groups font-size: 120% vertical-align: middle - + +// Individual Page #group-page .group-logo, .group-header text-align: center @@ -68,7 +70,4 @@ background-image: none padding-top: 0 padding-bottom: 0 - -// .hubs .active_table .active_table_node.inactive.closed a, .hubs .active_table .active_table_node.inactive.closed a *, .hubs .active_table .active_table_node.inactive.open a, .hubs .active_table .active_table_node.inactive.open a * - \ No newline at end of file diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index 7f7937edd4..0ed24b0d9d 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -1,124 +1,63 @@ %div.contact-container{bindonce: true} - - if @group.email.present? || @group.website.present? || @group.phone.present? %div.modal-centered %p.modal-header Contact - %p - - if @group.phone.present? + - if @group.phone.present? + %p Call %a{tel: @group.phone} = @group.phone - - if @group.email.present? - Email - %a{mailto: true, href: @group.email.reverse} - = @group.email.gsub('@', ' at ').gsub('.', ' dot ') - - if @group.website.present? - Website - %a{href: @group.website} + - if @group.email.present? + %p + %a{mailto: true, href: @group.email.reverse, target: "_blank" } + = @group.email + - if @group.website.present? + %p + %a{href: @group.website, target: "_blank" } = @group.website +%div{bindonce: true} + - if @group.facebook.present? + %div.modal-centered + %p.modal-header Follow + .follow-icons{bindonce: true} + %span + - if @group.twitter.present? + %a{href: "http://twitter.com/"+@group.twitter, target: "_blank"} + %i.ofn-i_041-twitter + %span + - if @group.facebook.present? + %a{href: @group.facebook, target: "_blank"} + %i.ofn-i_044-facebook + %span + - if @group.linkedin.present? + %a{href: @group.linkedin, target: "_blank"} + %i.ofn-i_042-linkedin + %span + - if @group.instagram.present? + %a{href: "http://instagram.com/"+@group.instagram, target: "_blank"} + %i.ofn-i_043-instagram + + %span{"ng-if" => "enterprise.instagram"} + %a{"ng-href" => "http://instagram.com/{{enterprise.instagram}}", target: "_blank"} + %i.ofn-i_043-instagram -%div.contact-container{bindonce: true} - / - if @group.email || @group.website || @group.phone - %div.modal-centered - %p.modal-header Address - %p - Container for address info - / = @group.phone - -%div.contact-container{bindonce: true} - / - if @group.email || @group.website || @group.phone - %div.modal-centered - %p.modal-header Follow - %p - Container for follow us info - / = @group.phone - - / %p.word-wrap{"ng-if" => "enterprise.email"} - / %a{"ng-href" => "{{enterprise.email | stripUrl}}", target: "_blank", mailto: true} - / %span.email - / {{ enterprise.email | stripUrl }} - - / %p.word-wrap{"ng-if" => "enterprise.website"} - / %a{"ng-href" => "http://{{enterprise.website | stripUrl}}", target: "_blank" } - / {{ enterprise.website | stripUrl }} +%div{bindonce: true} + - if @group.address.address1.present? || @group.address.address2.present? || @group.address.city.present? || @group.address.city.present? || @group.address.state.present? || @group.address.zipcode.present? + %div.modal-centered + %p.modal-header Address + %p + - if @group.address.address1.present? && @group.address.address1 != 'undefined' + = @group.address.address1 + - if @group.address.address2.present? && @group.address.address2 != 'undefined' + %br + = @group.address.address2 + %br + - if @group.address.city.present? && @group.address.city != 'undefined' + = @group.address.city + - if @group.address.state.present? && @group.address.state != 'undefined' + = @group.address.state + - if @group.address.zipcode? && @group.address.zipcode != 'undefined' + = @group.address.zipcode - / %h4 Contact us - / - if @group.phone - / .row - / .small-2.columns - / Call - / .small-10.columns - / = @group.phone - / - if @group.email - / .row - / .small-2.columns - / Email - / .small-10.columns - / = @group.email - / - if @group.website - / .row - / .small-2.columns - / Website - / .small-10.columns - / = @group.website - / %p   - / %h6 Address - / %p - / = @group.address.address1 - / - if @group.address.address2 - / %br - / = @group.address.address2 - / %br - / = @group.address.city - / , - / = @group.address.state - / = @group.address.zipcode - / %br - / = @group.address.country - / %p - / %h6 Follow us - / - if @group.facebook - / .row - / .small-2.columns - / Facebook - / .small-10.columns - / = @group.facebook - / - if @group.instagram - / .row - / .small-2.columns - / Instagram - / .small-10.columns - / = @group.instagram - / - if @group.linkedin - / .row - / .small-2.columns - / LinkedIn - / .small-10.columns - / = @group.linkedin - / - if @group.twitter - / .row - / .small-2.columns - / Twitter - / .small-10.columns - / = @group.twitter - - / .small-12.columns.pad-top - / .row.pad-top - / .small-12.columns.text-center.small - / %hr - / Copyright this year - / = @group.name - / %p - / -if @group.facebook - / %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} - / %i.ofn-i_044-facebook - / -if @group.email - / %a{title:'Email us', href: @group.email.reverse, mailto: true} - / %i.ofn-i_050-mail-circle - / -if @group.website - / %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} - / %i.ofn-i_049-web - / %p - /   diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 09268230bc..19cccd920b 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -19,11 +19,13 @@ name: "group{{group.id}}", id: "group{{group.id}}"} .row.pad-top{bindonce: true} - .small-6.columns + .small-12.medium-6.columns %a{"ng-href" => "groups/{{group.id}}"} %i.ofn-i_035-groups {{ group.name }} - .small-6.columns + .small-12.medium-3.columns + {{ group.address.state }} + .small-12.medium-3.columns {{ group.description }} .group{"ng-show" => "groups.length == 0"} From 504a0536932bdbb9d499c73fc9bc81fd31703d22 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 5 Feb 2015 17:21:41 +1100 Subject: [PATCH 040/384] link_to_ext helper for group pages --- app/helpers/groups_helper.rb | 17 ++++++++++++++++ app/views/groups/_contact.html.haml | 3 +-- spec/helpers/groups_helper_spec.rb | 31 +++++++++++++++++++---------- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index c091b2fc82..3b0e0022a2 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -1,2 +1,19 @@ module GroupsHelper + + def link_to_ext(url) + link_to strip_url(url), ext_url(url), target: '_blank' + end + + def ext_url(url) + if (url =~ /^https?:\/\//i) + return url + else + return 'http://' + url + end + end + + def strip_url(url) + url.andand.sub(/^https?:\/\//i, '') + end + end diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index 0ed24b0d9d..e1bda90404 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -13,8 +13,7 @@ = @group.email - if @group.website.present? %p - %a{href: @group.website, target: "_blank" } - = @group.website + =link_to_ext @group.website %div{bindonce: true} - if @group.facebook.present? diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 08a4335bae..c9f44dd95a 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -1,15 +1,24 @@ require 'spec_helper' -# Specs in this file have access to a helper object that includes -# the GroupsHelper. For example: -# -# describe GroupsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end describe GroupsHelper do - pending "add some examples to (or delete) #{__FILE__}" + describe "ext_url" do + it "adds http:// if missing" do + expect(helper.ext_url("http://example.com/")).to eq("http://example.com/") + expect(helper.ext_url("https://example.com/")).to eq("https://example.com/") + expect(helper.ext_url("example.com")).to eq("http://example.com") + end + end + describe "strip_url" do + it "removes http(s)://" do + expect(helper.strip_url("http://example.com/")).to eq("example.com/") + expect(helper.strip_url("https://example.com/")).to eq("example.com/") + expect(helper.strip_url("example.com")).to eq("example.com") + end + end + describe "link_to_ext" do + it "gives a link to an html external url" do + expect(helper.link_to_ext("example.com")).to eq('example.com') + expect(helper.link_to_ext("https://example.com/")).to eq('example.com/') + end + end end From 30601b9203634b40ec57bd55d771a38979e00d0b Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 5 Feb 2015 20:20:28 +1100 Subject: [PATCH 041/384] tidy up group contact with helpers --- app/helpers/groups_helper.rb | 18 ++++++++--- app/models/enterprise_group.rb | 30 ++++++++++-------- app/views/groups/_contact.html.haml | 48 +++++++++++------------------ app/views/groups/show.html.haml | 15 ++++----- spec/helpers/groups_helper_spec.rb | 13 ++++---- 5 files changed, 61 insertions(+), 63 deletions(-) diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 3b0e0022a2..ba884ec8db 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -1,14 +1,24 @@ module GroupsHelper - def link_to_ext(url) - link_to strip_url(url), ext_url(url), target: '_blank' + def link_to_url(url, html_options = {}) + link_to_service 'http://', url, html_options do + strip_url url + end end - def ext_url(url) + def link_to_service(baseurl, name, html_options = {}) + if name.empty? then return end + html_options = html_options.merge target: '_blank' + link_to ext_url(baseurl, name), html_options do + yield + end + end + + def ext_url(prefix, url) if (url =~ /^https?:\/\//i) return url else - return 'http://' + url + return prefix + url end end diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index ccbd79f198..7012e208ed 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -54,20 +54,24 @@ class EnterpriseGroup < ActiveRecord::Base address.phone.andand.sub('undefined', '') end - def facebook_url - if (facebook.blank?) then - return nil - end - if is_url? facebook then - facebook - else - 'https://www.facebook.com/' + facebook - end + def address1 + address.address1.andand.sub('undefined', '') end - private - - def is_url?(s) - s.andand.include? '://' + def address2 + address.address2.andand.sub('undefined', '') end + + def city + address.city.andand.sub('undefined', '') + end + + def state + address.state + end + + def zipcode + address.zipcode.andand.sub('undefined', '') + end + end diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index e1bda90404..9cd0babf7e 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -9,54 +9,42 @@ = @group.phone - if @group.email.present? %p - %a{mailto: true, href: @group.email.reverse, target: "_blank" } + =link_to_service "", @group.email.reverse, mailto: true do = @group.email - if @group.website.present? %p - =link_to_ext @group.website + =link_to_url @group.website %div{bindonce: true} - - if @group.facebook.present? + - if @group.facebook.present? || @group.twitter.present? || @group.linkedin.present? || @group.instagram.present? %div.modal-centered %p.modal-header Follow .follow-icons{bindonce: true} %span - - if @group.twitter.present? - %a{href: "http://twitter.com/"+@group.twitter, target: "_blank"} - %i.ofn-i_041-twitter + =link_to_service "http://twitter.com/", @group.twitter do + %i.ofn-i_041-twitter %span - - if @group.facebook.present? - %a{href: @group.facebook, target: "_blank"} - %i.ofn-i_044-facebook + =link_to_service "https://www.facebook.com/", @group.facebook do + %i.ofn-i_044-facebook %span - - if @group.linkedin.present? - %a{href: @group.linkedin, target: "_blank"} - %i.ofn-i_042-linkedin + =link_to_service "https://www.linkedin.com/in/", @group.linkedin do + %i.ofn-i_042-linkedin %span - - if @group.instagram.present? - %a{href: "http://instagram.com/"+@group.instagram, target: "_blank"} - %i.ofn-i_043-instagram + =link_to_service "http://instagram.com/", @group.instagram do + %i.ofn-i_043-instagram - %span{"ng-if" => "enterprise.instagram"} - %a{"ng-href" => "http://instagram.com/{{enterprise.instagram}}", target: "_blank"} - %i.ofn-i_043-instagram - %div{bindonce: true} - - if @group.address.address1.present? || @group.address.address2.present? || @group.address.city.present? || @group.address.city.present? || @group.address.state.present? || @group.address.zipcode.present? + - if @group.address1.present? || @group.city.present? %div.modal-centered %p.modal-header Address %p - - if @group.address.address1.present? && @group.address.address1 != 'undefined' - = @group.address.address1 - - if @group.address.address2.present? && @group.address.address2 != 'undefined' + = @group.address1 + - if @group.address2.present? %br - = @group.address.address2 + = @group.address2 %br - - if @group.address.city.present? && @group.address.city != 'undefined' - = @group.address.city - - if @group.address.state.present? && @group.address.state != 'undefined' - = @group.address.state - - if @group.address.zipcode? && @group.address.zipcode != 'undefined' - = @group.address.zipcode + = @group.city + = @group.state + = @group.zipcode diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 9dc60a7384..8e66f18a12 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -152,15 +152,12 @@ %p.text-small = "Copyright #{Date.today.year} #{@group.name}" %h2 - -if @group.facebook - %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} - %i.ofn-i_044-facebook - -if @group.email - %a{title:'Email us', href: @group.email.reverse, mailto: true} - %i.ofn-i_050-mail-circle - -if @group.website - %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} - %i.ofn-i_049-web + =link_to_service "https://www.facebook.com/", @group.facebook, title: 'Follow us on Facebook' do + %i.ofn-i_044-facebook + =link_to_service "", @group.email.reverse, title:'Email us', mailto: true do + %i.ofn-i_050-mail-circle + =link_to_service "http://", @group.website, title: 'Visit our website' do + %i.ofn-i_049-web %p   diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index c9f44dd95a..89b28ae3fa 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -2,10 +2,9 @@ require 'spec_helper' describe GroupsHelper do describe "ext_url" do - it "adds http:// if missing" do - expect(helper.ext_url("http://example.com/")).to eq("http://example.com/") - expect(helper.ext_url("https://example.com/")).to eq("https://example.com/") - expect(helper.ext_url("example.com")).to eq("http://example.com") + it "adds prefix if missing" do + expect(helper.ext_url("http://example.com/", "http://example.com/bla")).to eq("http://example.com/bla") + expect(helper.ext_url("http://example.com/", "bla")).to eq("http://example.com/bla") end end describe "strip_url" do @@ -15,10 +14,10 @@ describe GroupsHelper do expect(helper.strip_url("example.com")).to eq("example.com") end end - describe "link_to_ext" do + describe "link_to_url" do it "gives a link to an html external url" do - expect(helper.link_to_ext("example.com")).to eq('example.com') - expect(helper.link_to_ext("https://example.com/")).to eq('example.com/') + expect(helper.link_to_url("example.com")).to eq('example.com') + expect(helper.link_to_url("https://example.com/")).to eq('example.com/') end end end From 9547f91f46c63861614c2ab52bef1573a39939e8 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 6 Feb 2015 13:31:33 +1100 Subject: [PATCH 042/384] Groups page rejig the layout to make contact column skinnier in most use cases. Obscure email and website with CTA words for fixed width on content. Styling for anchor fix global issue --- app/assets/stylesheets/darkswarm/groups.css.sass | 6 +++--- app/assets/stylesheets/darkswarm/typography.css.sass | 2 +- app/views/groups/_contact.html.haml | 7 ++++--- app/views/groups/show.html.haml | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 0c5aa8483a..7cfcb3a443 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -33,17 +33,17 @@ max-height: 100px // Tabs - .tabs dd a + .tabs dd a // Mobile first padding: 0.35rem 0.5rem font-size: 0.75rem border: none margin-bottom: -2px margin-right: 2px - @media screen and (min-width: 400px) + @media screen and (min-width: 768px) .tabs dd a padding: 0.45rem 0.75rem font-size: 0.875rem - @media screen and (min-width: 768px) + @media screen and (min-width: 1024px) .tabs dd a padding: 1rem 2rem font-size: 1rem diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index eb1885d361..a3f7085675 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -22,7 +22,7 @@ body a color: $clr-brick - &:hover + &:hover, &:focus, &:active text-decoration: none color: $clr-brick-bright diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index 9cd0babf7e..cb6c25a6b5 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -4,16 +4,17 @@ %p.modal-header Contact - if @group.phone.present? %p - Call %a{tel: @group.phone} = @group.phone - if @group.email.present? %p =link_to_service "", @group.email.reverse, mailto: true do - = @group.email + Email us - if @group.website.present? %p - =link_to_url @group.website + %a{href:@group.website} + Visit our website + / =link_to_url @group.website %div{bindonce: true} - if @group.facebook.present? || @group.twitter.present? || @group.linkedin.present? || @group.instagram.present? diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 8e66f18a12..5c6a6eddd9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -13,7 +13,7 @@ .small-12.columns.pad-top .row.pad-top - .small-12.large-8.columns + .small-12.medium-8.large-9.columns %div{"ng-controller" => "TabsCtrl"} %tabset %tab{heading: 'Map', @@ -83,7 +83,7 @@ = render partial: 'shared/components/enterprise_no_results' - .small-12.large-4.columns + .small-12.medium-4.large-3.columns = render partial: 'contact' / %h4 Contact us / - if @group.phone From 755adf4287f30ccd2c64069df319010434b2fa30 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 6 Feb 2015 13:41:58 +1100 Subject: [PATCH 043/384] Index page stop-gap improvements until we get more variables to display in this list. Commented out search field not working --- app/views/groups/index.html.haml | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 19cccd920b..49bd277532 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -8,25 +8,32 @@ .small-12.columns %h1 Groups / regions %p - - %input.animate-show{type: :text, - "ng-model" => "query", - placeholder: "Search name or keyword", - "ng-debounce" => "150", - "ofn-disable-enter" => true} + / %input.animate-show{type: :text, + / "ng-model" => "query", + / placeholder: "Search name or keyword", + / "ng-debounce" => "150", + / "ofn-disable-enter" => true} .group{"ng-repeat" => "group in groups = (Groups.groups | groups:query | orderBy:order)", name: "group{{group.id}}", id: "group{{group.id}}"} .row.pad-top{bindonce: true} - .small-12.medium-6.columns - %a{"ng-href" => "groups/{{group.id}}"} + .small-2.medium-1.columns + %h1 %i.ofn-i_035-groups - {{ group.name }} - .small-12.medium-3.columns - {{ group.address.state }} - .small-12.medium-3.columns - {{ group.description }} + .small-10.medium-11.columns + %h4 + %a{"ng-href" => "groups/{{group.id}}"} + {{ group.name }} + %p + %em + {{ group.description }} + / .small-12.medium-3.columns + / {{ group.address.state }} + / .small-6.columns.text-right + / %p + / %em + / {{ group.description }} .group{"ng-show" => "groups.length == 0"} .row.pad-top From f292be8c92c00ee0f5f4721a736a4567ec5f1439 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 6 Feb 2015 13:50:34 +1100 Subject: [PATCH 044/384] Making styling of underlines for contact same color as HRs, bit lighter and brigher --- app/assets/stylesheets/darkswarm/modal-enterprises.css.sass | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass b/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass index 305566ad58..e0090ca163 100644 --- a/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass +++ b/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass @@ -15,7 +15,7 @@ font-size: 1rem font-weight: 400 color: $disabled-dark - border-bottom: 1px solid $disabled-dark + border-bottom: 1px solid $light-grey margin-top: 0.75rem margin-bottom: 0.5rem @@ -67,8 +67,8 @@ margin-bottom: 0.5rem overflow-y: scroll overflow-x: hidden - border-bottom: 1px solid #999 - @include box-shadow(0 2px 2px -2px #999) + border-bottom: 1px solid $light-grey + @include box-shadow(0 2px 2px -2px $light-grey) .enterprise-logo, img float: left From 78877f591bab57ebec5bdab79b26be0fa8155bcd Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 6 Feb 2015 13:58:04 +1100 Subject: [PATCH 045/384] Fix template so website links work to push to external pages --- app/views/groups/_contact.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index cb6c25a6b5..8a612ae194 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -12,7 +12,7 @@ Email us - if @group.website.present? %p - %a{href:@group.website} + %a{href:"http://"+@group.website, target: "_blank"} Visit our website / =link_to_url @group.website From 90ba1d219820e1b955fc69021b1870a1ff2593dc Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 6 Feb 2015 17:15:40 +1100 Subject: [PATCH 046/384] Updating the groups header to behave better with a square thumbnail logo image. Intention is for a square image constrained by PaperClip as per enterprise logos (later down the track) --- app/assets/images/noimage/group.png | Bin 17189 -> 25072 bytes .../stylesheets/darkswarm/groups.css.sass | 10 ++++++++-- app/views/groups/show.html.haml | 13 ++++++++----- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/assets/images/noimage/group.png b/app/assets/images/noimage/group.png index fe402076c004a8349900db9660ab1371e4fe8663..db71856deb9743be94549e4635c4c4fa27640a9a 100644 GIT binary patch delta 24441 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�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 RN;9gb!x@?buk@Yb{s$e$l^Os5 delta 16496 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 JKPn=^{}-y(^E3be diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 7cfcb3a443..4b8e9f0540 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -24,13 +24,19 @@ #group-page .group-logo, .group-header text-align: center + .group-logo + padding-bottom: 1em + max-height: 200px .group-name border-bottom: 1px solid #ccc @media screen and (min-width: 768px) .group-logo, .group-header text-align: left - .group-logo img - max-height: 100px + .group-logo + max-height: 120px + float: left + padding-right: 1em + background-color: white // Tabs .tabs dd a // Mobile first diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 5c6a6eddd9..0b66707871 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -5,15 +5,18 @@ .small-12.columns %img{"src" => @group.promo_image} .row - .small-12.medium-2.large-2.columns.group-logo.pad-top - %img{"src" => @group.logo} - .small-12.medium-10.large-10.columns.group-header.pad-top + / .small-6.medium-2.large-1.columns.group-logo.pad-top + + / .small-6.medium-10.large-11.columns.group-header.pad-top + + .small-12.columns.group-header.pad-top + %img.group-logo{"src" => @group.logo} %h2.group-name= @group.name %p= @group.description .small-12.columns.pad-top .row.pad-top - .small-12.medium-8.large-9.columns + .small-12.medium-12.large-9.columns %div{"ng-controller" => "TabsCtrl"} %tabset %tab{heading: 'Map', @@ -83,7 +86,7 @@ = render partial: 'shared/components/enterprise_no_results' - .small-12.medium-4.large-3.columns + .small-12.medium-12.large-3.columns = render partial: 'contact' / %h4 Contact us / - if @group.phone From 9e51b19f98e6690e4f0a3fcb085ca231748ef3ad Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Sat, 7 Feb 2015 18:11:51 +1100 Subject: [PATCH 047/384] right mapping of contributer's names in git --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000..0e268a507c --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ +Rob Harrington +Laura Summers From 2293623d2c72be358bc526b5c28d346aa41ce292 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Sat, 7 Feb 2015 19:24:36 +1100 Subject: [PATCH 048/384] Display default group logo if no logo was uploaded --- app/views/groups/show.html.haml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 0b66707871..fcf64a0892 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -3,14 +3,18 @@ %header .row .small-12.columns - %img{"src" => @group.promo_image} + - if @group.promo_image.present? + %img{"src" => @group.promo_image} .row / .small-6.medium-2.large-1.columns.group-logo.pad-top / .small-6.medium-10.large-11.columns.group-header.pad-top .small-12.columns.group-header.pad-top - %img.group-logo{"src" => @group.logo} + - if @group.logo.present? + %img.group-logo{"src" => @group.logo} + - else + %img.group-logo{"src" => '/assets/noimage/group.png'} %h2.group-name= @group.name %p= @group.description From 211e9c33a760b06521fb08bb86bd40be331c6eac Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Sat, 7 Feb 2015 19:31:16 +1100 Subject: [PATCH 049/384] Fixing link to group website --- app/helpers/groups_helper.rb | 6 ------ app/views/groups/_contact.html.haml | 3 +-- spec/helpers/groups_helper_spec.rb | 6 ------ 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index ba884ec8db..8b6ac9f5fd 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -1,11 +1,5 @@ module GroupsHelper - def link_to_url(url, html_options = {}) - link_to_service 'http://', url, html_options do - strip_url url - end - end - def link_to_service(baseurl, name, html_options = {}) if name.empty? then return end html_options = html_options.merge target: '_blank' diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index 8a612ae194..458aa81dbe 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -12,9 +12,8 @@ Email us - if @group.website.present? %p - %a{href:"http://"+@group.website, target: "_blank"} + =link_to_service "http://", @group.website do Visit our website - / =link_to_url @group.website %div{bindonce: true} - if @group.facebook.present? || @group.twitter.present? || @group.linkedin.present? || @group.instagram.present? diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 89b28ae3fa..69e7a3add4 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -14,10 +14,4 @@ describe GroupsHelper do expect(helper.strip_url("example.com")).to eq("example.com") end end - describe "link_to_url" do - it "gives a link to an html external url" do - expect(helper.link_to_url("example.com")).to eq('example.com') - expect(helper.link_to_url("https://example.com/")).to eq('example.com/') - end - end end From edcef020170a7dc422852b262871b73ae2bfac56 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Sat, 7 Feb 2015 19:40:59 +1100 Subject: [PATCH 050/384] link_to_service helper deals with nil --- app/helpers/groups_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 8b6ac9f5fd..6ca71a7b78 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -1,7 +1,7 @@ module GroupsHelper def link_to_service(baseurl, name, html_options = {}) - if name.empty? then return end + if name.blank? then return end html_options = html_options.merge target: '_blank' link_to ext_url(baseurl, name), html_options do yield From 0fdf313424fd9812af6b645501b71c707221f20f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 12 Feb 2015 10:31:13 +1100 Subject: [PATCH 051/384] Fixing img display of producers if no logo present --- app/views/producers/_fat.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml index f420b33386..17304abbe3 100644 --- a/app/views/producers/_fat.html.haml +++ b/app/views/producers/_fat.html.haml @@ -4,7 +4,7 @@ / Will add in long description available once clean up HTML formatting producer.long_description %div{"bo-if" => "producer.description"} %label About us - %img.right.show-for-medium-up{src: "{{ producer.logo }}" } + %img.right.show-for-medium-up{"ng-src" => "{{producer.logo}}" } %p.text-small {{ producer.description }} %div.show-for-medium-up{"bo-if" => "producer.description.length==0"} From 773a5480e904e48420496ea71510255e2a67e91d Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 12 Feb 2015 10:44:20 +1100 Subject: [PATCH 052/384] Activate search input for groups again --- app/views/groups/index.html.haml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 49bd277532..1688446a7f 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -8,11 +8,11 @@ .small-12.columns %h1 Groups / regions %p - / %input.animate-show{type: :text, - / "ng-model" => "query", - / placeholder: "Search name or keyword", - / "ng-debounce" => "150", - / "ofn-disable-enter" => true} + %input.animate-show{type: :text, + "ng-model" => "query", + placeholder: "Search name or keyword", + "ng-debounce" => "150", + "ofn-disable-enter" => true} .group{"ng-repeat" => "group in groups = (Groups.groups | groups:query | orderBy:order)", name: "group{{group.id}}", From 57e838898fa30427f5d0ee1f191df1b0bd082cc9 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 12 Feb 2015 11:05:21 +1100 Subject: [PATCH 053/384] Update documentation of database setup db:setup is doing db:schema and db:seed already --- README.markdown | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index 8c9883dc04..5448e34328 100644 --- a/README.markdown +++ b/README.markdown @@ -53,14 +53,11 @@ Configure the site: cp config/application.yml.example config/application.yml edit config/application.yml -Create the development and test databases, using the settings specified in `config/database.yml`: +Create the development and test databases, using the settings specified in `config/database.yml` +and load data from `db/seeds.rb`: rake db:setup -Then load the schema and some seed data with the following command: - - rake db:schema:load db:seed - Load some default data for your environment: rake openfoodnetwork:dev:load_sample_data @@ -99,4 +96,4 @@ usage instructions. ## Licence -Copyright (c) 2012 - 2013 Open Food Foundation, released under the AGPL licence. +Copyright (c) 2012 - 2015 Open Food Foundation, released under the AGPL licence. From 6b5a1255f896b77a7715c0a6f54c0146e523a446 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 12 Feb 2015 16:36:18 +1100 Subject: [PATCH 054/384] fixing a bunch of tests --- app/models/enterprise_group.rb | 23 +- .../admin/enterprise_groups/index.html.haml | 2 +- app/views/groups/show.html.haml | 223 ------------------ lib/spree/api/testing_support/setup.rb | 2 + spec/features/consumer/groups_spec.rb | 5 +- spec/models/enterprise_group_spec.rb | 11 - spec/models/model_set_spec.rb | 10 +- 7 files changed, 26 insertions(+), 250 deletions(-) diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 7012e208ed..60c9162a77 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -6,6 +6,7 @@ class EnterpriseGroup < ActiveRecord::Base belongs_to :address, :class_name => 'Spree::Address' accepts_nested_attributes_for :address validates :address, presence: true, associated: true + before_validation :set_undefined_address_fields before_validation :set_unused_address_fields validates :name, presence: true @@ -50,28 +51,38 @@ class EnterpriseGroup < ActiveRecord::Base address.firstname = address.lastname = 'unused' if address.present? end + def set_undefined_address_fields + if !address.present? + return + end + address.phone.present? || address.phone = 'undefined' + address.address1.present? || address.address1 = 'undefined' + address.city.present? || address.city = 'undefined' + address.zipcode.present? || address.zipcode = 'undefined' + end + def phone - address.phone.andand.sub('undefined', '') + address.andand.phone.andand.sub('undefined', '') end def address1 - address.address1.andand.sub('undefined', '') + address.andand.address1.andand.sub('undefined', '') end def address2 - address.address2.andand.sub('undefined', '') + address.andand.address2.andand.sub('undefined', '') end def city - address.city.andand.sub('undefined', '') + address.andand.city.andand.sub('undefined', '') end def state - address.state + address.andand.state end def zipcode - address.zipcode.andand.sub('undefined', '') + address.andand.zipcode.andand.sub('undefined', '') end end diff --git a/app/views/admin/enterprise_groups/index.html.haml b/app/views/admin/enterprise_groups/index.html.haml index 3ccf82c10c..13bc19e364 100644 --- a/app/views/admin/enterprise_groups/index.html.haml +++ b/app/views/admin/enterprise_groups/index.html.haml @@ -32,5 +32,5 @@ .blank-action - else = link_to_with_icon 'icon-arrow-down', '', main_app.admin_enterprise_group_move_down_path(enterprise_group), class: 'move-down no-text' - - if enterprise_group.first? + - if !enterprise_group.first? = link_to_with_icon 'icon-arrow-up', '', main_app.admin_enterprise_group_move_up_path(enterprise_group), class: 'move-up no-text' diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index fcf64a0892..98de889532 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -6,10 +6,6 @@ - if @group.promo_image.present? %img{"src" => @group.promo_image} .row - / .small-6.medium-2.large-1.columns.group-logo.pad-top - - / .small-6.medium-10.large-11.columns.group-header.pad-top - .small-12.columns.group-header.pad-top - if @group.logo.present? %img.group-logo{"src" => @group.logo} @@ -92,65 +88,6 @@ .small-12.medium-12.large-3.columns = render partial: 'contact' - / %h4 Contact us - / - if @group.phone - / .row - / .small-2.columns - / Call - / .small-10.columns - / = @group.phone - / - if @group.email - / .row - / .small-2.columns - / Email - / .small-10.columns - / = @group.email - / - if @group.website - / .row - / .small-2.columns - / Website - / .small-10.columns - / = @group.website - / %p   - / %h6 Address - / %p - / = @group.address.address1 - / - if @group.address.address2 - / %br - / = @group.address.address2 - / %br - / = @group.address.city - / , - / = @group.address.state - / = @group.address.zipcode - / %br - / = @group.address.country - / %p - / %h6 Follow us - / - if @group.facebook - / .row - / .small-2.columns - / Facebook - / .small-10.columns - / = @group.facebook - / - if @group.instagram - / .row - / .small-2.columns - / Instagram - / .small-10.columns - / = @group.instagram - / - if @group.linkedin - / .row - / .small-2.columns - / LinkedIn - / .small-10.columns - / = @group.linkedin - / - if @group.twitter - / .row - / .small-2.columns - / Twitter - / .small-10.columns - / = @group.twitter .small-12.columns.pad-top .row.pad-top @@ -168,164 +105,4 @@ %p   -// ORIGINAL MIKAEL STUFF - -/ %div{style: "padding: 1.5em;"} -/ %img{"src" => @group.promo_image, style: "display: block"} -/ %div{style: "margin-bottom: 1em"} -/ %img{"src" => @group.logo} -/ %div{style: "display: inline-block; vertical-align: middle"} -/ %h2{style: "margin-bottom: 0em"}= @group.name -/ %div= @group.description - -/ #div{"ng-controller" => "TabsCtrl", style: "clear: both"} -/ %tabset - -/ %tab{heading: 'Map', -/ active: "active(\'\')", -/ select: "select(\'\')"} -/ = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer -/ .map-container -/ %map{"ng-controller" => "MapCtrl", "ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} -/ %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} -/ %markers{models: "OfnMap.enterprises", fit: "true", -/ coords: "'self'", icon: "'icon'", click: "'reveal'"} - -/ %tab{heading: 'About us', -/ active: "active(\'about\')", -/ select: "select(\'about\')"} -/ %h3 About us -/ %p= @group.long_description - -/ %tab{heading: 'Our producers', -/ active: "active(\'producers\')", -/ select: "select(\'producers\')"} -/ .producers.pad-top{"ng-controller" => "GroupEnterprisesCtrl"} -/ .row -/ .small-12.columns.pad-top -/ %h1 Our Producers -/ = render partial: "shared/components/enterprise_search" -/ -# TODO: find out why this is not working -/ -#= render partial: "producers/filters" - -/ .row{bindonce: true} -/ .small-12.columns -/ .active_table -/ %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", -/ "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)", -/ "ng-controller" => "GroupEnterpriseNodeCtrl", -/ "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", -/ id: "{{producer.hash}}"} - -/ .small-12.columns -/ = render partial: 'producers/skinny' -/ = render partial: 'producers/fat' - -/ = render partial: 'shared/components/enterprise_no_results' - -/ %tab{heading: 'Our hubs', -/ active: "active(\'hubs\')", -/ select: "select(\'hubs\')"} -/ #hubs.hubs{"ng-controller" => "GroupEnterprisesCtrl"} -/ .row -/ .small-12.columns -/ %h1 Our Hubs - -/ = render partial: "shared/components/enterprise_search" -/ -# TODO: find out why this is not working -/ -#= render partial: "home/filters" - -/ .row{bindonce: true} -/ .small-12.columns -/ .active_table -/ %hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}", -/ "ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", -/ "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", -/ "ng-controller" => "GroupEnterpriseNodeCtrl"} -/ .small-12.columns -/ = render partial: 'home/skinny' -/ = render partial: 'home/fat' - -/ = render partial: 'shared/components/enterprise_no_results' - -/ %tab{heading: 'Contact us', -/ active: "active(\'contact\')", -/ select: "select(\'contact\')"} -/ .row -/ .small-6.columns -/ %h3 Contact us -/ - if @group.phone -/ .row -/ .small-2.columns -/ Call -/ .small-10.columns -/ = @group.phone -/ - if @group.email -/ .row -/ .small-2.columns -/ Email -/ .small-10.columns -/ = @group.email -/ - if @group.website -/ .row -/ .small-2.columns -/ Website -/ .small-10.columns -/ = @group.website -/ .small-6.columns -/ %h3 Follow us -/ - if @group.facebook -/ .row -/ .small-2.columns -/ Facebook -/ .small-10.columns -/ = @group.facebook -/ - if @group.instagram -/ .row -/ .small-2.columns -/ Instagram -/ .small-10.columns -/ = @group.instagram -/ - if @group.linkedin -/ .row -/ .small-2.columns -/ LinkedIn -/ .small-10.columns -/ = @group.linkedin -/ - if @group.twitter -/ .row -/ .small-2.columns -/ Twitter -/ .small-10.columns -/ = @group.twitter -/ .row -/ .small-6.columns -/ %h3 Address -/ %p -/ = @group.address.address1 -/ - if @group.address.address2 -/ %br -/ = @group.address.address2 -/ %br -/ = @group.address.city -/ , -/ = @group.address.state -/ = @group.address.zipcode -/ %br -/ = @group.address.country - -/ .text-center -/ %p -/ = @group.name -/ %p -/ -if @group.facebook -/ %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/' + @group.facebook, target: '_blank'} -/ %i.ofn-i_044-facebook -/ -if @group.email -/ %a{title:'Email us', href: @group.email.reverse, mailto: true} -/ %i.ofn-i_050-mail-circle -/ -if @group.website -/ %a{title:'Visit our website', href: 'http://' + @group.website, target: '_blank'} -/ %i.ofn-i_049-web - = render partial: "shared/footer" diff --git a/lib/spree/api/testing_support/setup.rb b/lib/spree/api/testing_support/setup.rb index cbf8393e33..2b96b60946 100644 --- a/lib/spree/api/testing_support/setup.rb +++ b/lib/spree/api/testing_support/setup.rb @@ -7,6 +7,7 @@ module Spree user = stub_model(Spree::LegacyUser) user.stub(:has_spree_role?).with("admin").and_return(false) user.stub(:enterprises) { [] } + user.stub(:owned_groups) { [] } user end end @@ -33,6 +34,7 @@ module Spree # Stub enterprises, needed for cancan ability checks user.stub(:enterprises) { [] } + user.stub(:owned_groups) { [] } user end diff --git a/spec/features/consumer/groups_spec.rb b/spec/features/consumer/groups_spec.rb index b2db10d58c..e51ac39ea2 100644 --- a/spec/features/consumer/groups_spec.rb +++ b/spec/features/consumer/groups_spec.rb @@ -14,9 +14,6 @@ feature 'Groups', js: true do it "renders enterprise modals for groups" do visit groups_path - page.should have_content enterprise.name - open_enterprise_modal enterprise - modal_should_be_open_for enterprise - page.should have_content "Herndon, Vic" + page.should have_content group.name end end diff --git a/spec/models/enterprise_group_spec.rb b/spec/models/enterprise_group_spec.rb index 9642b146c7..ceca13b13b 100644 --- a/spec/models/enterprise_group_spec.rb +++ b/spec/models/enterprise_group_spec.rb @@ -61,15 +61,4 @@ describe EnterpriseGroup do EnterpriseGroup.managed_by(user).should == [eg1] end end - - describe "urls" do - it 'provides a Facebook URL' do - g = create(:enterprise_group) - g.facebook_url.should be_nil - g.facebook = 'test' - g.facebook_url.should == 'https://www.facebook.com/test' - g.facebook = 'http://fb.com/test' - g.facebook_url.should == g.facebook - end - end end diff --git a/spec/models/model_set_spec.rb b/spec/models/model_set_spec.rb index 24f452412f..e45018747c 100644 --- a/spec/models/model_set_spec.rb +++ b/spec/models/model_set_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' describe ModelSet do describe "updating" do it "creates new models" do - attrs = {collection_attributes: {'1' => {name: 'e1', description: 'foo'}, - '2' => {name: 'e2', description: 'bar'}}} + attrs = {collection_attributes: {'1' => {name: 's1'}, + '2' => {name: 's2'}}} - ms = ModelSet.new(EnterpriseGroup, EnterpriseGroup.all, attrs) + ms = ModelSet.new(Suburb, Suburb.all, attrs) - expect { ms.save }.to change(EnterpriseGroup, :count).by(2) + expect { ms.save }.to change(Suburb, :count).by(2) - EnterpriseGroup.where(name: ['e1', 'e2']).count.should == 2 + Suburb.where(name: ['s1', 's2']).count.should == 2 end From 87686848bccd1745f659fb358bc758c9e4b10599 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 13 Feb 2015 15:39:44 +1100 Subject: [PATCH 055/384] make group contact fields not null --- ...0121030627_add_web_conact_to_enterprise_groups.rb | 10 ---------- ...121030627_add_web_contact_to_enterprise_groups.rb | 10 ++++++++++ db/schema.rb | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 db/migrate/20150121030627_add_web_conact_to_enterprise_groups.rb create mode 100644 db/migrate/20150121030627_add_web_contact_to_enterprise_groups.rb diff --git a/db/migrate/20150121030627_add_web_conact_to_enterprise_groups.rb b/db/migrate/20150121030627_add_web_conact_to_enterprise_groups.rb deleted file mode 100644 index 1aca6a0e16..0000000000 --- a/db/migrate/20150121030627_add_web_conact_to_enterprise_groups.rb +++ /dev/null @@ -1,10 +0,0 @@ -class AddWebConactToEnterpriseGroups < ActiveRecord::Migration - def change - add_column :enterprise_groups, :email, :string - add_column :enterprise_groups, :website, :string - add_column :enterprise_groups, :facebook, :string - add_column :enterprise_groups, :instagram, :string - add_column :enterprise_groups, :linkedin, :string - add_column :enterprise_groups, :twitter, :string - end -end diff --git a/db/migrate/20150121030627_add_web_contact_to_enterprise_groups.rb b/db/migrate/20150121030627_add_web_contact_to_enterprise_groups.rb new file mode 100644 index 0000000000..afea2dbeea --- /dev/null +++ b/db/migrate/20150121030627_add_web_contact_to_enterprise_groups.rb @@ -0,0 +1,10 @@ +class AddWebContactToEnterpriseGroups < ActiveRecord::Migration + def change + add_column :enterprise_groups, :email, :string, null: false, default: '' + add_column :enterprise_groups, :website, :string, null: false, default: '' + add_column :enterprise_groups, :facebook, :string, null: false, default: '' + add_column :enterprise_groups, :instagram, :string, null: false, default: '' + add_column :enterprise_groups, :linkedin, :string, null: false, default: '' + add_column :enterprise_groups, :twitter, :string, null: false, default: '' + end +end diff --git a/db/schema.rb b/db/schema.rb index c92989be2f..fe9d3f8ca2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -198,12 +198,12 @@ ActiveRecord::Schema.define(:version => 20150202000203) do t.integer "logo_file_size" t.datetime "logo_updated_at" t.integer "address_id" - t.string "email" - t.string "website" - t.string "facebook" - t.string "instagram" - t.string "linkedin" - t.string "twitter" + t.string "email", :default => "", :null => false + t.string "website", :default => "", :null => false + t.string "facebook", :default => "", :null => false + t.string "instagram", :default => "", :null => false + t.string "linkedin", :default => "", :null => false + t.string "twitter", :default => "", :null => false t.integer "owner_id" end From 9496987da6789bd203412b6fedf173d3ddba7dc3 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 18 Feb 2015 12:04:17 +1100 Subject: [PATCH 056/384] Fix display of admin group side menu --- .../controllers/side_menu_controller.js.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee index 63f05a9376..b30b160bd0 100644 --- a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee @@ -12,3 +12,6 @@ angular.module("admin.enterprise_groups") ] $scope.select(0) + + $scope.showItem = (item) -> + true From 08afcac1e58f036678ff621e04a118f4a7637ec5 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 18 Feb 2015 14:55:42 +1100 Subject: [PATCH 057/384] exposing more group attributes to angular --- app/views/groups/index.html.haml | 3 +++ app/views/json/_groups.rabl | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 1688446a7f..24b0389a89 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -28,6 +28,9 @@ %p %em {{ group.description }} + /{{ group.state }} + /{{ group.email }} + /{{ group.twitter }} / .small-12.medium-3.columns / {{ group.address.state }} / .small-6.columns.text-right diff --git a/app/views/json/_groups.rabl b/app/views/json/_groups.rabl index 691d36f42f..a079290e23 100644 --- a/app/views/json/_groups.rabl +++ b/app/views/json/_groups.rabl @@ -1,5 +1,5 @@ collection @groups -attributes :id, :name, :position, :description, :long_description +attributes :id, :name, :position, :description, :long_description, :email, :website, :facebook, :instagram, :linkedin, :twitter child enterprises: :enterprises do attributes :id @@ -12,3 +12,7 @@ end node :promo_image do |group| group.promo_image(:large) if group.promo_image.exists? end + +node :state do |group| + group.state().andand.abbr +end From 346a4e71d048a4de9c263dc9f4e87f26d14ec510 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 10:17:58 +1100 Subject: [PATCH 058/384] side menu partial does not require showItem defined --- .../controllers/side_menu_controller.js.coffee | 3 --- app/views/admin/shared/_side_menu.html.haml | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee index b30b160bd0..63f05a9376 100644 --- a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee @@ -12,6 +12,3 @@ angular.module("admin.enterprise_groups") ] $scope.select(0) - - $scope.showItem = (item) -> - true diff --git a/app/views/admin/shared/_side_menu.html.haml b/app/views/admin/shared/_side_menu.html.haml index ff5d086570..12d1013825 100644 --- a/app/views/admin/shared/_side_menu.html.haml +++ b/app/views/admin/shared/_side_menu.html.haml @@ -2,7 +2,7 @@ %a.menu_item{ href: "", id: "{{ item.name.toLowerCase().replace(' ', '_') }}", ng: { repeat: '(index,item) in menu.items | filter:{visible:true}', click: 'select(index)', - show: 'showItem(item)', + show: 'showItem(item) !== false', class: '{ selected: item.selected}', 'class-odd' => "'odd'", 'class-even' => "'even'" } } From bbac5aa8037015d75ce9cca82f55c8f247a5a03b Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 10:22:37 +1100 Subject: [PATCH 059/384] Using pure css for styling odd and even menu items --- app/assets/stylesheets/admin/side_menu.css.sass | 4 ++-- app/views/admin/shared/_side_menu.html.haml | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/admin/side_menu.css.sass b/app/assets/stylesheets/admin/side_menu.css.sass index 6c03059d27..c218cfbcf0 100644 --- a/app/assets/stylesheets/admin/side_menu.css.sass +++ b/app/assets/stylesheets/admin/side_menu.css.sass @@ -7,9 +7,9 @@ font-size: 120% cursor: pointer text-transform: uppercase - &.odd + &:nth-child(odd) background-color: #ebf3fb - &.even + &:nth-child(even) background-color: #ffffff &:hover background-color: #eaf0f5 diff --git a/app/views/admin/shared/_side_menu.html.haml b/app/views/admin/shared/_side_menu.html.haml index 12d1013825..44efb2475c 100644 --- a/app/views/admin/shared/_side_menu.html.haml +++ b/app/views/admin/shared/_side_menu.html.haml @@ -3,8 +3,6 @@ ng: { repeat: '(index,item) in menu.items | filter:{visible:true}', click: 'select(index)', show: 'showItem(item) !== false', - class: '{ selected: item.selected}', - 'class-odd' => "'odd'", - 'class-even' => "'even'" } } + class: '{ selected: item.selected }' } } %i{ class: "{{item.icon_class}}" } %span {{ item.name }} From 718a5911a3ea7086b3407251962bbfa1febc34b2 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 10:48:39 +1100 Subject: [PATCH 060/384] code style and cleanup --- .../group_enterprise_node_controller.js.coffee | 2 +- .../darkswarm/controllers/tabs_controller.js.coffee | 2 +- app/helpers/groups_helper.rb | 8 ++++---- app/models/enterprise_group.rb | 6 ++---- app/views/admin/enterprise_groups/_form.html.haml | 6 +++--- ...add_address_instances_to_existing_enterprise_groups.rb | 4 +--- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/group_enterprise_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/group_enterprise_node_controller.js.coffee index 31c2354a53..376320e458 100644 --- a/app/assets/javascripts/darkswarm/controllers/group_enterprise_node_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/group_enterprise_node_controller.js.coffee @@ -2,7 +2,7 @@ Darkswarm.controller "GroupEnterpriseNodeCtrl", ($scope, CurrentHub) -> $scope.active = false - $scope.toggle = () -> + $scope.toggle = -> $scope.active = !$scope.active $scope.open = -> diff --git a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee index 74ce167580..fad13164ff 100644 --- a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee @@ -4,7 +4,7 @@ Darkswarm.controller "TabsCtrl", ($scope, $rootScope, $location) -> $location.hash() == path # Select tab by setting the url hash path. - $scope.select= (path)-> + $scope.select = (path)-> $location.hash path # Toggle tab selected status by setting the url hash path. diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 6ca71a7b78..c5de5c1ca0 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -1,7 +1,7 @@ module GroupsHelper def link_to_service(baseurl, name, html_options = {}) - if name.blank? then return end + return if name.blank? html_options = html_options.merge target: '_blank' link_to ext_url(baseurl, name), html_options do yield @@ -9,10 +9,10 @@ module GroupsHelper end def ext_url(prefix, url) - if (url =~ /^https?:\/\//i) - return url + if url =~ /^https?:\/\//i + url else - return prefix + url + prefix + url end end diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 60c9162a77..582518e208 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -43,7 +43,7 @@ class EnterpriseGroup < ActiveRecord::Base if user.has_spree_role?('admin') scoped else - where('owner_id = ?', user.id); + where('owner_id = ?', user.id) end } @@ -52,9 +52,7 @@ class EnterpriseGroup < ActiveRecord::Base end def set_undefined_address_fields - if !address.present? - return - end + return unless address.present? address.phone.present? || address.phone = 'undefined' address.address1.present? || address.address1 = 'undefined' address.city.present? || address.city = 'undefined' diff --git a/app/views/admin/enterprise_groups/_form.html.haml b/app/views/admin/enterprise_groups/_form.html.haml index 9ebf6db147..c72c566e46 100644 --- a/app/views/admin/enterprise_groups/_form.html.haml +++ b/app/views/admin/enterprise_groups/_form.html.haml @@ -1,4 +1,4 @@ -= render :partial => 'spree/shared/error_messages', :locals => { :target => @enterprise } += render 'spree/shared/error_messages', target: @enterprise = form_for [main_app, :admin, @enterprise_group] do |f| .row{ ng: {app: 'admin.enterprise_groups', controller: 'enterpriseGroupCtrl'} } @@ -7,5 +7,5 @@ = render 'admin/shared/side_menu' .one.column   .eleven.columns.omega.fullwidth_inputs - = render :partial => 'inputs', :locals => { :f => f } - = render partial: "spree/admin/shared/#{action}_resource_links" + = render 'inputs', f: f + = render "spree/admin/shared/#{action}_resource_links" diff --git a/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb b/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb index 2978d286dc..0800948126 100644 --- a/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb +++ b/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb @@ -3,9 +3,7 @@ class AddAddressInstancesToExistingEnterpriseGroups < ActiveRecord::Migration country = Spree::Country.find_by_name(ENV['DEFAULT_COUNTRY']) state = country.states.first EnterpriseGroup.all.each do |g| - if g.address.present? then - next - end + next if g.address.present? address = Spree::Address.new(firstname: 'unused', lastname: 'unused', address1: 'undefined', city: 'undefined', zipcode: 'undefined', state: state, country: country, phone: 'undefined') g.address = address g.save From ecd7b16ef515584127a5867c58e9a13a82cff4c6 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 11:09:43 +1100 Subject: [PATCH 061/384] revert setting default country by name in enterprise controller --- app/controllers/admin/enterprises_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index ac852c0886..503fd736ba 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -59,7 +59,7 @@ module Admin def build_resource_with_address enterprise = build_resource_without_address enterprise.address = Spree::Address.new - enterprise.address.country = Spree::Country.find_by_name(ENV['DEFAULT_COUNTRY']) + enterprise.address.country = Spree::Country.find_by_id(Spree::Config[:default_country_id]) enterprise end alias_method_chain :build_resource, :address From a0f0b3c93b2cc84dea36e28178a39dcacf3c22dd Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 11:14:25 +1100 Subject: [PATCH 062/384] display only activated enterprises to add to groups --- app/views/admin/enterprise_groups/_inputs.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml index 60181e09c4..a9e911700a 100644 --- a/app/views/admin/enterprise_groups/_inputs.html.haml +++ b/app/views/admin/enterprise_groups/_inputs.html.haml @@ -28,7 +28,7 @@ = f.field_container :enterprise_ids do = f.label :enterprise_ids, 'Enterprises' %br/ - = f.collection_select :enterprise_ids, Enterprise.all, :id, :name, {}, {class: "select2 fullwidth", multiple: true} + = f.collection_select :enterprise_ids, Enterprise.activated, :id, :name, {}, {class: "select2 fullwidth", multiple: true} %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='About'" } } %legend About From 8e83c6679a47ff7fcf2dcb857f6702437438ae15 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 11:25:31 +1100 Subject: [PATCH 063/384] extended test of owned groups --- spec/models/spree/user_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index a1a9f3c940..8510b89e0e 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -30,9 +30,11 @@ describe Spree.user_class do let(:u2) { create(:user) } let!(:g1) { create(:enterprise_group, owner: u1) } let!(:g2) { create(:enterprise_group, owner: u1) } + let!(:g3) { create(:enterprise_group, owner: u2) } it "provides access to owned groups" do - expect(u1.owned_groups(:reload)).to include g1, g2 + expect(u1.owned_groups(:reload)).to eq [g1, g2] + expect(u2.owned_groups(:reload)).to eq [g3] end end From 6a29b830c261c6dba8ecf604f95f174831aefcbc Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 11:35:06 +1100 Subject: [PATCH 064/384] commenting changes in overridden adaptivemenu.js --- vendor/assets/javascripts/jquery.adaptivemenu.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vendor/assets/javascripts/jquery.adaptivemenu.js b/vendor/assets/javascripts/jquery.adaptivemenu.js index 8df8b98c65..b9a611a8fb 100644 --- a/vendor/assets/javascripts/jquery.adaptivemenu.js +++ b/vendor/assets/javascripts/jquery.adaptivemenu.js @@ -10,7 +10,7 @@ jQuery.fn.AdaptiveMenu = function(options){ var options = jQuery.extend({ text: "More...", - accuracy:0, + accuracy:0, // originally 70, but not needed anymore 'class':null, 'classLinckMore':null },options); @@ -26,6 +26,7 @@ jQuery.fn.AdaptiveMenu = function(options){ }); var buildingMenu = function(){ + // Using parent width instead of given window width var windowWidth = $(menu.parent()).width() - options.accuracy; for(var i = 0; i windowWidth ) @@ -55,6 +56,7 @@ jQuery.fn.AdaptiveMenu = function(options){ } } + // calling buildingMenu without parameter jQuery(window).width() jQuery(window).resize(buildingMenu); jQuery(window).ready(buildingMenu); From a7b723af93684ee9e594a6f7b5e74d8873c633fd Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 13:23:50 +1100 Subject: [PATCH 065/384] owner index for enterprise groups --- .../20150115050935_add_addresses_ref_to_enterprise_groups.rb | 2 +- db/migrate/20150202000203_add_owner_to_enterprise_groups.rb | 2 +- .../20150219021742_add_owner_index_to_enterprise_groups.rb | 5 +++++ db/schema.rb | 4 +++- 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20150219021742_add_owner_index_to_enterprise_groups.rb diff --git a/db/migrate/20150115050935_add_addresses_ref_to_enterprise_groups.rb b/db/migrate/20150115050935_add_addresses_ref_to_enterprise_groups.rb index 8b2ca415b1..566b35bb88 100644 --- a/db/migrate/20150115050935_add_addresses_ref_to_enterprise_groups.rb +++ b/db/migrate/20150115050935_add_addresses_ref_to_enterprise_groups.rb @@ -1,6 +1,6 @@ class AddAddressesRefToEnterpriseGroups < ActiveRecord::Migration def change add_column :enterprise_groups, :address_id, :integer - add_foreign_key :enterprise_groups, :spree_addresses, name: "enterprise_groups_address_id_fk", column: "address_id" + add_foreign_key :enterprise_groups, :spree_addresses, column: "address_id" end end diff --git a/db/migrate/20150202000203_add_owner_to_enterprise_groups.rb b/db/migrate/20150202000203_add_owner_to_enterprise_groups.rb index 82ffdf9f5d..f2e46452df 100644 --- a/db/migrate/20150202000203_add_owner_to_enterprise_groups.rb +++ b/db/migrate/20150202000203_add_owner_to_enterprise_groups.rb @@ -1,6 +1,6 @@ class AddOwnerToEnterpriseGroups < ActiveRecord::Migration def change add_column :enterprise_groups, :owner_id, :integer - add_foreign_key :enterprise_groups, :spree_users, name: "enterprise_groups_owner_id_fk", column: "owner_id" + add_foreign_key :enterprise_groups, :spree_users, column: "owner_id" end end diff --git a/db/migrate/20150219021742_add_owner_index_to_enterprise_groups.rb b/db/migrate/20150219021742_add_owner_index_to_enterprise_groups.rb new file mode 100644 index 0000000000..062e9523e6 --- /dev/null +++ b/db/migrate/20150219021742_add_owner_index_to_enterprise_groups.rb @@ -0,0 +1,5 @@ +class AddOwnerIndexToEnterpriseGroups < ActiveRecord::Migration + def change + add_index :enterprise_groups, :owner_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 5c567fb57a..959e0f06d6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150202000203) do +ActiveRecord::Schema.define(:version => 20150219021742) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -207,6 +207,8 @@ ActiveRecord::Schema.define(:version => 20150202000203) do t.integer "owner_id" end + add_index "enterprise_groups", ["owner_id"], :name => "index_enterprise_groups_on_owner_id" + create_table "enterprise_groups_enterprises", :id => false, :force => true do |t| t.integer "enterprise_group_id" t.integer "enterprise_id" From d5c2abdd7ea8e6f0eb7c2a648ced2eef0edd7077 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 16:33:43 +1100 Subject: [PATCH 066/384] stripping "undefined" in after_find and after_save callbacks instead of overriding getters --- app/models/enterprise_group.rb | 32 ++++++------------- .../admin/enterprise_groups/_inputs.html.haml | 4 --- app/views/groups/_contact.html.haml | 9 +++--- 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 582518e208..aaf33b2272 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -8,6 +8,8 @@ class EnterpriseGroup < ActiveRecord::Base validates :address, presence: true, associated: true before_validation :set_undefined_address_fields before_validation :set_unused_address_fields + after_find :unset_undefined_address_fields + after_save :unset_undefined_address_fields validates :name, presence: true validates :description, presence: true @@ -18,7 +20,7 @@ class EnterpriseGroup < ActiveRecord::Base attr_accessible :address_attributes attr_accessible :email, :website, :facebook, :instagram, :linkedin, :twitter - delegate :phone, :to => :address + delegate :phone, :address1, :address2, :city, :zipcode, :state, :country, :to => :address has_attached_file :logo, styles: {medium: "100x100"}, @@ -59,28 +61,12 @@ class EnterpriseGroup < ActiveRecord::Base address.zipcode.present? || address.zipcode = 'undefined' end - def phone - address.andand.phone.andand.sub('undefined', '') - end - - def address1 - address.andand.address1.andand.sub('undefined', '') - end - - def address2 - address.andand.address2.andand.sub('undefined', '') - end - - def city - address.andand.city.andand.sub('undefined', '') - end - - def state - address.andand.state - end - - def zipcode - address.andand.zipcode.andand.sub('undefined', '') + def unset_undefined_address_fields + return unless address.present? + address.phone.sub!(/^undefined$/, '') + address.address1.sub!(/^undefined$/, '') + address.city.sub!(/^undefined$/, '') + address.zipcode.sub!(/^undefined$/, '') end end diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml index a9e911700a..02f5aa337c 100644 --- a/app/views/admin/enterprise_groups/_inputs.html.haml +++ b/app/views/admin/enterprise_groups/_inputs.html.haml @@ -63,13 +63,11 @@ .row .alpha.three.columns = af.label :phone - %span.required * .omega.eight.columns = af.text_field :phone, { placeholder: "eg. 98 7654 3210"} .row .three.columns.alpha = af.label :address1 - %span.required * .eight.columns.omega = af.text_field :address1, { placeholder: "eg. 123 High Street"} .row @@ -82,7 +80,6 @@ = af.label :city, 'Suburb' \/ = af.label :zipcode, 'Postcode' - %span.required * .four.columns = af.text_field :city, { placeholder: "eg. Northcote"} .four.columns.omega @@ -92,7 +89,6 @@ = af.label :state_id, 'State' \/ = af.label :country_id, 'Country' - %span.required * .four.columns = af.collection_select :state_id, af.object.country.states, :id, :name, {}, :class => "select2 fullwidth" .four.columns.omega diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index 458aa81dbe..c3da5e26ba 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -42,9 +42,10 @@ - if @group.address2.present? %br = @group.address2 - %br - = @group.city - = @group.state - = @group.zipcode + - if @group.city.present? + %br + = @group.city + = @group.state + = @group.zipcode From c01d45e3af9c7d7894647283894f91229cedfef6 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 16:51:58 +1100 Subject: [PATCH 067/384] Serialize move_up and move_down methods of groups There have been race conditions in other projects using the acts_as_list gem which could be solved by serializing. --- .../admin/enterprise_groups_controller.rb | 12 ++++++++---- app/models/enterprise_group.rb | 2 ++ lib/open_food_network/locking.rb | 13 +++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 lib/open_food_network/locking.rb diff --git a/app/controllers/admin/enterprise_groups_controller.rb b/app/controllers/admin/enterprise_groups_controller.rb index 3968cc5289..7759f57c12 100644 --- a/app/controllers/admin/enterprise_groups_controller.rb +++ b/app/controllers/admin/enterprise_groups_controller.rb @@ -7,14 +7,18 @@ module Admin end def move_up - @enterprise_group = EnterpriseGroup.find params[:enterprise_group_id] - @enterprise_group.move_higher + EnterpriseGroup.with_isolation_level_serializable do + @enterprise_group = EnterpriseGroup.find params[:enterprise_group_id] + @enterprise_group.move_higher + end redirect_to main_app.admin_enterprise_groups_path end def move_down - @enterprise_group = EnterpriseGroup.find params[:enterprise_group_id] - @enterprise_group.move_lower + EnterpriseGroup.with_isolation_level_serializable do + @enterprise_group = EnterpriseGroup.find params[:enterprise_group_id] + @enterprise_group.move_lower + end redirect_to main_app.admin_enterprise_groups_path end diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index aaf33b2272..9d510d8f2b 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -1,3 +1,5 @@ +require 'open_food_network/locking' + class EnterpriseGroup < ActiveRecord::Base acts_as_list diff --git a/lib/open_food_network/locking.rb b/lib/open_food_network/locking.rb new file mode 100644 index 0000000000..8c24256bf3 --- /dev/null +++ b/lib/open_food_network/locking.rb @@ -0,0 +1,13 @@ +module OpenFoodNetwork::Locking + # http://rhnh.net/2010/06/30/acts-as-list-will-break-in-production + def with_isolation_level_serializable + self.transaction do + self.connection.execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE") + yield + end + end +end + +class ActiveRecord::Base + extend OpenFoodNetwork::Locking +end From 98063dae60e56aeb68b8fededaf4680c91a7da30 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Feb 2015 16:57:19 +1100 Subject: [PATCH 068/384] setting default country by id in groups controller --- app/controllers/admin/enterprise_groups_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/enterprise_groups_controller.rb b/app/controllers/admin/enterprise_groups_controller.rb index 7759f57c12..32280b7fa3 100644 --- a/app/controllers/admin/enterprise_groups_controller.rb +++ b/app/controllers/admin/enterprise_groups_controller.rb @@ -27,7 +27,7 @@ module Admin def build_resource_with_address enterprise_group = build_resource_without_address enterprise_group.address = Spree::Address.new - enterprise_group.address.country = Spree::Country.find_by_name(ENV['DEFAULT_COUNTRY']) + enterprise_group.address.country = Spree::Country.find_by_id(Spree::Config[:default_country_id]) enterprise_group end alias_method_chain :build_resource, :address From 176db78e6470680dc1fb633481305ceee0ae7ed5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 20 Feb 2015 11:17:01 +1100 Subject: [PATCH 069/384] Extract data fetching from views --- app/controllers/admin/enterprise_groups_controller.rb | 11 +++++++++-- app/views/admin/enterprise_groups/_inputs.html.haml | 5 ++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/controllers/admin/enterprise_groups_controller.rb b/app/controllers/admin/enterprise_groups_controller.rb index 32280b7fa3..3f8888edfb 100644 --- a/app/controllers/admin/enterprise_groups_controller.rb +++ b/app/controllers/admin/enterprise_groups_controller.rb @@ -1,6 +1,7 @@ module Admin class EnterpriseGroupsController < ResourceController - before_filter :load_countries, :except => :index + before_filter :load_data, except: :index + before_filter :load_object_data, only: [:new, :edit, :create, :update] def index @enterprise_groups = @enterprise_groups.managed_by(spree_current_user) @@ -34,10 +35,16 @@ module Admin private - def load_countries + def load_data @countries = Spree::Country.order(:name) + @enterprises = Enterprise.activated end + def load_object_data + @owner_email = @enterprise_group.andand.owner.andand.email || "" + end + + def collection EnterpriseGroup.by_position end diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml index 02f5aa337c..dbf8606af6 100644 --- a/app/views/admin/enterprise_groups/_inputs.html.haml +++ b/app/views/admin/enterprise_groups/_inputs.html.haml @@ -17,8 +17,7 @@ .with-tip{'data-powertip' => "The primary user responsible for this group."} %a What's this? .eight.columns.omega - - owner_email = @enterprise_group.andand.owner.andand.email || "" - = f.hidden_field :owner_id, class: "select2 fullwidth", 'ofn-user-autocomplete' => true, email: owner_email + = f.hidden_field :owner_id, class: "select2 fullwidth", 'ofn-user-autocomplete' => true, email: @owner_email = f.field_container :on_front_page do = f.label :on_front_page, 'On front page?' @@ -28,7 +27,7 @@ = f.field_container :enterprise_ids do = f.label :enterprise_ids, 'Enterprises' %br/ - = f.collection_select :enterprise_ids, Enterprise.activated, :id, :name, {}, {class: "select2 fullwidth", multiple: true} + = f.collection_select :enterprise_ids, @enterprises, :id, :name, {}, {class: "select2 fullwidth", multiple: true} %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='About'" } } %legend About From 4554c0555d33e8cc99fc036b9a692e33d74120ee Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 20 Feb 2015 11:31:39 +1100 Subject: [PATCH 070/384] Change locking to use postgres syntax, fixes 'must be first statement in transaction' error --- lib/open_food_network/locking.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_food_network/locking.rb b/lib/open_food_network/locking.rb index 8c24256bf3..30dec63b0b 100644 --- a/lib/open_food_network/locking.rb +++ b/lib/open_food_network/locking.rb @@ -2,7 +2,7 @@ module OpenFoodNetwork::Locking # http://rhnh.net/2010/06/30/acts-as-list-will-break-in-production def with_isolation_level_serializable self.transaction do - self.connection.execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE") + self.connection.execute "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE" yield end end From f58219eb3d39423431c97db465beb32e42b05cbd Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 20 Feb 2015 11:36:30 +1100 Subject: [PATCH 071/384] Split inputs into separate partials for each fieldset --- .../admin/enterprise_groups/_form.html.haml | 6 +- .../enterprise_groups/_form_about.html.haml | 6 + .../enterprise_groups/_form_address.html.haml | 36 +++++ .../enterprise_groups/_form_images.html.haml | 18 +++ .../_form_primary_details.html.haml | 30 +++++ .../enterprise_groups/_form_web.html.haml | 32 +++++ .../admin/enterprise_groups/_inputs.html.haml | 127 ------------------ 7 files changed, 127 insertions(+), 128 deletions(-) create mode 100644 app/views/admin/enterprise_groups/_form_about.html.haml create mode 100644 app/views/admin/enterprise_groups/_form_address.html.haml create mode 100644 app/views/admin/enterprise_groups/_form_images.html.haml create mode 100644 app/views/admin/enterprise_groups/_form_primary_details.html.haml create mode 100644 app/views/admin/enterprise_groups/_form_web.html.haml delete mode 100644 app/views/admin/enterprise_groups/_inputs.html.haml diff --git a/app/views/admin/enterprise_groups/_form.html.haml b/app/views/admin/enterprise_groups/_form.html.haml index c72c566e46..1b96909d3e 100644 --- a/app/views/admin/enterprise_groups/_form.html.haml +++ b/app/views/admin/enterprise_groups/_form.html.haml @@ -7,5 +7,9 @@ = render 'admin/shared/side_menu' .one.column   .eleven.columns.omega.fullwidth_inputs - = render 'inputs', f: f + = render 'form_primary_details', f: f + = render 'form_about', f: f + = render 'form_images', f: f + = render 'form_address', f: f + = render 'form_web', f: f = render "spree/admin/shared/#{action}_resource_links" diff --git a/app/views/admin/enterprise_groups/_form_about.html.haml b/app/views/admin/enterprise_groups/_form_about.html.haml new file mode 100644 index 0000000000..60d7276094 --- /dev/null +++ b/app/views/admin/enterprise_groups/_form_about.html.haml @@ -0,0 +1,6 @@ +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='About'" } } + %legend About + = f.field_container :long_description do + = f.label :long_description + %br/ + = f.text_area :long_description diff --git a/app/views/admin/enterprise_groups/_form_address.html.haml b/app/views/admin/enterprise_groups/_form_address.html.haml new file mode 100644 index 0000000000..081af7110e --- /dev/null +++ b/app/views/admin/enterprise_groups/_form_address.html.haml @@ -0,0 +1,36 @@ += f.fields_for :address do |af| + %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Contact'" } } + %legend Contact + .row + .alpha.three.columns + = af.label :phone + .omega.eight.columns + = af.text_field :phone, { placeholder: "eg. 98 7654 3210"} + .row + .three.columns.alpha + = af.label :address1 + .eight.columns.omega + = af.text_field :address1, { placeholder: "eg. 123 High Street"} + .row + .alpha.three.columns + = af.label :address2 + .eight.columns.omega + = af.text_field :address2 + .row + .three.columns.alpha + = af.label :city, 'Suburb' + \/ + = af.label :zipcode, 'Postcode' + .four.columns + = af.text_field :city, { placeholder: "eg. Northcote"} + .four.columns.omega + = af.text_field :zipcode, { placeholder: "eg. 3070"} + .row + .three.columns.alpha + = af.label :state_id, 'State' + \/ + = af.label :country_id, 'Country' + .four.columns + = af.collection_select :state_id, af.object.country.states, :id, :name, {}, :class => "select2 fullwidth" + .four.columns.omega + = af.collection_select :country_id, available_countries, :id, :name, {}, :class => "select2 fullwidth" diff --git a/app/views/admin/enterprise_groups/_form_images.html.haml b/app/views/admin/enterprise_groups/_form_images.html.haml new file mode 100644 index 0000000000..49169851c3 --- /dev/null +++ b/app/views/admin/enterprise_groups/_form_images.html.haml @@ -0,0 +1,18 @@ +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Images'" } } + %legend Images + .row + .alpha.three.columns + = f.label :logo, class: 'with-tip', 'data-powertip' => 'This is the logo' + .with-tip{'data-powertip' => 'This is the logo'} + %a What's this? + .omega.eight.columns + = image_tag @object.logo.url if @object.logo.present? + = f.file_field :logo + .row + .alpha.three.columns + = f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed at the top of the Group profile' + .with-tip{'data-powertip' => 'This image is displayed at the top of the Group profile'} + %a What's this? + .omega.eight.columns + = image_tag @object.promo_image.url if @object.promo_image.present? + = f.file_field :promo_image diff --git a/app/views/admin/enterprise_groups/_form_primary_details.html.haml b/app/views/admin/enterprise_groups/_form_primary_details.html.haml new file mode 100644 index 0000000000..27dec0701c --- /dev/null +++ b/app/views/admin/enterprise_groups/_form_primary_details.html.haml @@ -0,0 +1,30 @@ +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Primary Details'" } } + %legend Primary Details + = f.field_container :name do + = f.label :name + %br/ + = f.text_field :name + + = f.field_container :description do + = f.label :description + %br/ + = f.text_field :description + + - if spree_current_user.admin? + .row + .three.columns.alpha + =f.label :owner_id, 'Owner' + .with-tip{'data-powertip' => "The primary user responsible for this group."} + %a What's this? + .eight.columns.omega + = f.hidden_field :owner_id, class: "select2 fullwidth", 'ofn-user-autocomplete' => true, email: @owner_email + + = f.field_container :on_front_page do + = f.label :on_front_page, 'On front page?' + %br/ + = f.check_box :on_front_page + + = f.field_container :enterprise_ids do + = f.label :enterprise_ids, 'Enterprises' + %br/ + = f.collection_select :enterprise_ids, @enterprises, :id, :name, {}, {class: "select2 fullwidth", multiple: true} diff --git a/app/views/admin/enterprise_groups/_form_web.html.haml b/app/views/admin/enterprise_groups/_form_web.html.haml new file mode 100644 index 0000000000..ea985259c3 --- /dev/null +++ b/app/views/admin/enterprise_groups/_form_web.html.haml @@ -0,0 +1,32 @@ +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Web'" } } + %legend Web Resources + .row + .alpha.three.columns + = f.label :website + .omega.eight.columns + = f.text_field :website, { placeholder: "eg. www.truffles.com"} + .row + .alpha.three.columns + = f.label :email + .omega.eight.columns + = f.text_field :email + .row + .alpha.three.columns + = f.label :facebook, 'Facebook' + .omega.eight.columns + = f.text_field :facebook + .row + .alpha.three.columns + = f.label :instagram, 'Instagram' + .omega.eight.columns + = f.text_field :instagram + .row + .alpha.three.columns + = f.label :linkedin, 'LinkedIn' + .omega.eight.columns + = f.text_field :linkedin + .row + .alpha.three.columns + = f.label :twitter + .omega.eight.columns + = f.text_field :twitter, { placeholder: "eg. @the_prof" } diff --git a/app/views/admin/enterprise_groups/_inputs.html.haml b/app/views/admin/enterprise_groups/_inputs.html.haml deleted file mode 100644 index dbf8606af6..0000000000 --- a/app/views/admin/enterprise_groups/_inputs.html.haml +++ /dev/null @@ -1,127 +0,0 @@ -%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Primary Details'" } } - %legend Primary Details - = f.field_container :name do - = f.label :name - %br/ - = f.text_field :name - - = f.field_container :description do - = f.label :description - %br/ - = f.text_field :description - - - if spree_current_user.admin? - .row - .three.columns.alpha - =f.label :owner_id, 'Owner' - .with-tip{'data-powertip' => "The primary user responsible for this group."} - %a What's this? - .eight.columns.omega - = f.hidden_field :owner_id, class: "select2 fullwidth", 'ofn-user-autocomplete' => true, email: @owner_email - - = f.field_container :on_front_page do - = f.label :on_front_page, 'On front page?' - %br/ - = f.check_box :on_front_page - - = f.field_container :enterprise_ids do - = f.label :enterprise_ids, 'Enterprises' - %br/ - = f.collection_select :enterprise_ids, @enterprises, :id, :name, {}, {class: "select2 fullwidth", multiple: true} - -%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='About'" } } - %legend About - = f.field_container :long_description do - = f.label :long_description - %br/ - = f.text_area :long_description - - -%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Images'" } } - %legend Images - .row - .alpha.three.columns - = f.label :logo, class: 'with-tip', 'data-powertip' => 'This is the logo' - .with-tip{'data-powertip' => 'This is the logo'} - %a What's this? - .omega.eight.columns - = image_tag @object.logo.url if @object.logo.present? - = f.file_field :logo - .row - .alpha.three.columns - = f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed at the top of the Group profile' - .with-tip{'data-powertip' => 'This image is displayed at the top of the Group profile'} - %a What's this? - .omega.eight.columns - = image_tag @object.promo_image.url if @object.promo_image.present? - = f.file_field :promo_image - -= f.fields_for :address do |af| - %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Contact'" } } - %legend Contact - .row - .alpha.three.columns - = af.label :phone - .omega.eight.columns - = af.text_field :phone, { placeholder: "eg. 98 7654 3210"} - .row - .three.columns.alpha - = af.label :address1 - .eight.columns.omega - = af.text_field :address1, { placeholder: "eg. 123 High Street"} - .row - .alpha.three.columns - = af.label :address2 - .eight.columns.omega - = af.text_field :address2 - .row - .three.columns.alpha - = af.label :city, 'Suburb' - \/ - = af.label :zipcode, 'Postcode' - .four.columns - = af.text_field :city, { placeholder: "eg. Northcote"} - .four.columns.omega - = af.text_field :zipcode, { placeholder: "eg. 3070"} - .row - .three.columns.alpha - = af.label :state_id, 'State' - \/ - = af.label :country_id, 'Country' - .four.columns - = af.collection_select :state_id, af.object.country.states, :id, :name, {}, :class => "select2 fullwidth" - .four.columns.omega - = af.collection_select :country_id, available_countries, :id, :name, {}, :class => "select2 fullwidth" - -%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Web'" } } - %legend Web Resources - .row - .alpha.three.columns - = f.label :website - .omega.eight.columns - = f.text_field :website, { placeholder: "eg. www.truffles.com"} - .row - .alpha.three.columns - = f.label :email - .omega.eight.columns - = f.text_field :email - .row - .alpha.three.columns - = f.label :facebook, 'Facebook' - .omega.eight.columns - = f.text_field :facebook - .row - .alpha.three.columns - = f.label :instagram, 'Instagram' - .omega.eight.columns - = f.text_field :instagram - .row - .alpha.three.columns - = f.label :linkedin, 'LinkedIn' - .omega.eight.columns - = f.text_field :linkedin - .row - .alpha.three.columns - = f.label :twitter - .omega.eight.columns - = f.text_field :twitter, { placeholder: "eg. @the_prof" } From f8ca24c5cdc92b94c1d34aefc58e1db6ddfa5643 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 20 Feb 2015 14:56:16 +1100 Subject: [PATCH 072/384] Index enterprise_groups address_id --- ...150220035501_add_address_id_index_to_enterprise_groups.rb | 5 +++++ db/schema.rb | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20150220035501_add_address_id_index_to_enterprise_groups.rb diff --git a/db/migrate/20150220035501_add_address_id_index_to_enterprise_groups.rb b/db/migrate/20150220035501_add_address_id_index_to_enterprise_groups.rb new file mode 100644 index 0000000000..32094cbde7 --- /dev/null +++ b/db/migrate/20150220035501_add_address_id_index_to_enterprise_groups.rb @@ -0,0 +1,5 @@ +class AddAddressIdIndexToEnterpriseGroups < ActiveRecord::Migration + def change + add_index :enterprise_groups, :address_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 959e0f06d6..a8ac91e6d8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150219021742) do +ActiveRecord::Schema.define(:version => 20150220035501) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -207,6 +207,7 @@ ActiveRecord::Schema.define(:version => 20150219021742) do t.integer "owner_id" end + add_index "enterprise_groups", ["address_id"], :name => "index_enterprise_groups_on_address_id" add_index "enterprise_groups", ["owner_id"], :name => "index_enterprise_groups_on_owner_id" create_table "enterprise_groups_enterprises", :id => false, :force => true do |t| @@ -584,9 +585,9 @@ ActiveRecord::Schema.define(:version => 20150219021742) do t.string "email" t.text "special_instructions" t.integer "distributor_id" + t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" - t.integer "order_cycle_id" t.integer "cart_id" end From 60b7a571c54c21085ba25e3a6f64570dc2ede71d Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 20 Feb 2015 15:35:07 +1100 Subject: [PATCH 073/384] Tweak styling and markup for contact info in right column. --- app/views/groups/_contact.html.haml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index c3da5e26ba..8b45ca8437 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -17,25 +17,25 @@ %div{bindonce: true} - if @group.facebook.present? || @group.twitter.present? || @group.linkedin.present? || @group.instagram.present? - %div.modal-centered + %div.modal-centered.pad-top %p.modal-header Follow .follow-icons{bindonce: true} %span - =link_to_service "http://twitter.com/", @group.twitter do - %i.ofn-i_041-twitter + =link_to_service "http://twitter.com/", @group.twitter do + %i.ofn-i_041-twitter %span - =link_to_service "https://www.facebook.com/", @group.facebook do - %i.ofn-i_044-facebook + =link_to_service "https://www.facebook.com/", @group.facebook do + %i.ofn-i_044-facebook %span - =link_to_service "https://www.linkedin.com/in/", @group.linkedin do - %i.ofn-i_042-linkedin + =link_to_service "https://www.linkedin.com/in/", @group.linkedin do + %i.ofn-i_042-linkedin %span - =link_to_service "http://instagram.com/", @group.instagram do - %i.ofn-i_043-instagram + =link_to_service "http://instagram.com/", @group.instagram do + %i.ofn-i_043-instagram %div{bindonce: true} - if @group.address1.present? || @group.city.present? - %div.modal-centered + %div.modal-centered.pad-top %p.modal-header Address %p = @group.address1 From 3ab961a7e2282593e1307a65c4e0ab2e7f7963d6 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 20 Feb 2015 16:14:40 +1100 Subject: [PATCH 074/384] New mixin for gradients --- app/assets/stylesheets/darkswarm/mixins.sass | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/darkswarm/mixins.sass b/app/assets/stylesheets/darkswarm/mixins.sass index 00aa601b2f..6925e84f72 100644 --- a/app/assets/stylesheets/darkswarm/mixins.sass +++ b/app/assets/stylesheets/darkswarm/mixins.sass @@ -120,5 +120,21 @@ background-repeat: no-repeat background-size: 100% auto - +@mixin gradient($gradient-clr1, $gradient-clr2) + background: $gradient-clr1 + // Old browsers + background: -moz-linear-gradient(top, $gradient-clr1 0%, $gradient-clr2 100%) + // FF3.6+ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, $gradient-clr1), color-stop(100%, $gradient-clr2)) + // Chrome,Safari4+ + background: -webkit-linear-gradient(top, $gradient-clr1 0%, $gradient-clr2 100%) + // Chrome10+,Safari5.1+ + background: -o-linear-gradient(top, $gradient-clr1 0%, $gradient-clr2 100%) + // Opera 11.10+ + background: -ms-linear-gradient(top, $gradient-clr1 0%, $gradient-clr2 100%) + // IE10+ + background: linear-gradient(to bottom, $gradient-clr1 0%, $gradient-clr2 100%) + // W3C + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$gradient-clr1', endColorstr='$gradient-clr2',GradientType=0 ) + // IE6-8 From 3aa06ee1e4ed50379b177d5121ab554546ddfa9e Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 20 Feb 2015 16:14:50 +1100 Subject: [PATCH 075/384] Tabs styling for groups page - making it pretty. --- app/assets/stylesheets/darkswarm/groups.css.sass | 15 +++++++++++---- app/views/groups/show.html.haml | 6 +++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 4b8e9f0540..6b5361c500 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -40,25 +40,32 @@ // Tabs .tabs dd a // Mobile first - padding: 0.35rem 0.5rem + padding: 0.25rem 0.45rem 0rem font-size: 0.75rem border: none margin-bottom: -2px margin-right: 2px + text-transform: capitalize + @include avenir + @include border-radius(1em 0.25em 0 0) + @include gradient($disabled-light, $disabled-bright) @media screen and (min-width: 768px) .tabs dd a - padding: 0.45rem 0.75rem + padding: 0.5rem 1rem 0.25em font-size: 0.875rem + @include border-radius(1.5em 0.25em 0 0) @media screen and (min-width: 1024px) .tabs dd a - padding: 1rem 2rem + padding: 0.75rem 1.5rem 0.5em font-size: 1rem + @include border-radius(2em 0.25em 0 0) .tabs dd.active a + @include gradient(white, white) margin-bottom: -1px border-top: 1px solid $light-grey border-left: 1px solid $light-grey border-right: 1px solid $light-grey - border-bottom: 1px solid white + border-bottom: 0 .tabs-content border-top: 1px solid $light-grey border-left: 1px solid $light-grey diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 98de889532..94a54d59df 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -14,8 +14,8 @@ %h2.group-name= @group.name %p= @group.description - .small-12.columns.pad-top - .row.pad-top + .small-12.columns + .row .small-12.medium-12.large-9.columns %div{"ng-controller" => "TabsCtrl"} %tabset @@ -32,7 +32,7 @@ %tab{heading: 'About us', active: "active(\'about\')", select: "select(\'about\')"} - %h3.pad-top About us + %h1 About Us %p= @group.long_description %tab{heading: 'Our producers', From 985887334fa06ad9b227ce600f2dd3cdd641cd58 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 20 Feb 2015 16:43:03 +1100 Subject: [PATCH 076/384] Tweak the padding between header and tabs. --- app/views/groups/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 94a54d59df..dfa780f688 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -14,7 +14,7 @@ %h2.group-name= @group.name %p= @group.description - .small-12.columns + .small-12.columns.pad-top .row .small-12.medium-12.large-9.columns %div{"ng-controller" => "TabsCtrl"} From e05d64a0b43344fa7160e13940b24bde34f302f7 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 20 Feb 2015 16:43:36 +1100 Subject: [PATCH 077/384] WIP on groups index page. Needs work from Maikel or Rohan. --- .../stylesheets/darkswarm/groups.css.sass | 4 ++ app/views/groups/index.html.haml | 66 ++++++++++++------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 6b5361c500..039678de77 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -9,6 +9,10 @@ background-repeat: no-repeat padding-bottom: 20px + a > .group-name + &:hover, &:focus, &:active + text-decoration: underline + .group padding-bottom: 0.5em .row div diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 24b0389a89..d1013cfed4 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -7,36 +7,58 @@ #active-table-search.row.pad-top .small-12.columns %h1 Groups / regions - %p - %input.animate-show{type: :text, - "ng-model" => "query", - placeholder: "Search name or keyword", - "ng-debounce" => "150", - "ofn-disable-enter" => true} + / TODO: Maikel this search input still doesn't work. + / %p + / %input.animate-show{type: :text, + / "ng-model" => "query", + / placeholder: "Search name or keyword", + / "ng-debounce" => "150", + / "ofn-disable-enter" => true} .group{"ng-repeat" => "group in groups = (Groups.groups | groups:query | orderBy:order)", name: "group{{group.id}}", id: "group{{group.id}}"} .row.pad-top{bindonce: true} - .small-2.medium-1.columns - %h1 + .small-12.medium-6.columns + %a{"ng-href" => "groups/{{group.id}}"} %i.ofn-i_035-groups - .small-10.medium-11.columns - %h4 - %a{"ng-href" => "groups/{{group.id}}"} + %span.group-name {{ group.name }} + / %br + / %small + / %em + / {{ group.description }} + .small-4.medium-2.columns %p - %em - {{ group.description }} - /{{ group.state }} - /{{ group.email }} - /{{ group.twitter }} - / .small-12.medium-3.columns - / {{ group.address.state }} - / .small-6.columns.text-right - / %p - / %em - / {{ group.description }} + {{ group.state }} + .small-8.medium-4.columns.text-right + + / TODO: Maikel can we use the same logic on contacts partial to render these? doesn't work with the angular binding... + + / .follow-icons{bindonce: true} + / %span + / =link_to_service "http://twitter.com/", @group.twitter do + / %i.ofn-i_041-twitter + / %span + / =link_to_service "https://www.facebook.com/", @group.facebook do + / %i.ofn-i_044-facebook + / %span + / =link_to_service "https://www.linkedin.com/in/", @group.linkedin do + / %i.ofn-i_042-linkedin + / %span + / =link_to_service "http://instagram.com/", @group.instagram do + / %i.ofn-i_043-instagram + + %p + {{ group.facebook }} + %br + {{ group.twitter }} + %br + {{ group.instagram }} + %br + {{ group.email }} + %br + {{ group.website }} .group{"ng-show" => "groups.length == 0"} .row.pad-top From a522242e7a0c8ba6259076588ed500b9752d61df Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Sat, 21 Feb 2015 18:43:03 +1100 Subject: [PATCH 078/384] dealing with invalid live data in migration --- ...dd_address_instances_to_existing_enterprise_groups.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb b/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb index 0800948126..23fc038ce8 100644 --- a/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb +++ b/db/migrate/20150115050936_add_address_instances_to_existing_enterprise_groups.rb @@ -6,7 +6,14 @@ class AddAddressInstancesToExistingEnterpriseGroups < ActiveRecord::Migration next if g.address.present? address = Spree::Address.new(firstname: 'unused', lastname: 'unused', address1: 'undefined', city: 'undefined', zipcode: 'undefined', state: state, country: country, phone: 'undefined') g.address = address - g.save + # some groups are invalid, because of a missing description + g.save!(validate: false) end end + + def self.down + # we can't know which addresses were already there and which weren't + # and we can't remove addresses as long as they are referenced and + # required by the model + end end From eade6890704ed84a46ad26c2b27eb2ba21477521 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Sun, 22 Feb 2015 16:15:04 +1100 Subject: [PATCH 079/384] linkToService directive to generate external links --- .../directives/link_to_service.js.coffee | 8 +++++ .../darkswarm/filters/ext_url.js.coffee | 9 ++++++ app/views/groups/index.html.haml | 32 ++++--------------- 3 files changed, 23 insertions(+), 26 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/directives/link_to_service.js.coffee create mode 100644 app/assets/javascripts/darkswarm/filters/ext_url.js.coffee diff --git a/app/assets/javascripts/darkswarm/directives/link_to_service.js.coffee b/app/assets/javascripts/darkswarm/directives/link_to_service.js.coffee new file mode 100644 index 0000000000..dc0f513588 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/link_to_service.js.coffee @@ -0,0 +1,8 @@ +Darkswarm.directive "linkToService", -> + restrict: 'E' + replace: true + scope: { + ref: '=' + service: '=' + } + template: '' diff --git a/app/assets/javascripts/darkswarm/filters/ext_url.js.coffee b/app/assets/javascripts/darkswarm/filters/ext_url.js.coffee new file mode 100644 index 0000000000..8d4b8b90fe --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/ext_url.js.coffee @@ -0,0 +1,9 @@ +Darkswarm.filter "ext_url", () -> + urlPattern = /^https?:\/\// + (url, prefix) -> + if (!url) + return url + if (url.match(urlPattern)) + return url + else + return prefix + url diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index d1013cfed4..6115410b43 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -32,33 +32,13 @@ %p {{ group.state }} .small-8.medium-4.columns.text-right - - / TODO: Maikel can we use the same logic on contacts partial to render these? doesn't work with the angular binding... - - / .follow-icons{bindonce: true} - / %span - / =link_to_service "http://twitter.com/", @group.twitter do - / %i.ofn-i_041-twitter - / %span - / =link_to_service "https://www.facebook.com/", @group.facebook do - / %i.ofn-i_044-facebook - / %span - / =link_to_service "https://www.linkedin.com/in/", @group.linkedin do - / %i.ofn-i_042-linkedin - / %span - / =link_to_service "http://instagram.com/", @group.instagram do - / %i.ofn-i_043-instagram - %p - {{ group.facebook }} - %br - {{ group.twitter }} - %br - {{ group.instagram }} - %br - {{ group.email }} - %br - {{ group.website }} + %link-to-service.ofn-i_050-mail-circle{service: '""', ref: 'group.email.split("").reverse().join("")', mailto: true} + %link-to-service.ofn-i_049-web{service: '"http://"', ref: 'group.website'} + %link-to-service.ofn-i_041-twitter{service: '"http://twitter.com/"', ref: 'group.twitter'} + %link-to-service.ofn-i_044-facebook{service: '"https://www.facebook.com/"', ref: 'group.facebook'} + %link-to-service.ofn-i_042-linkedin{service: '"https://www.linkedin.com/in/"', ref: 'group.linkedin'} + %link-to-service.ofn-i_043-instagram{service: '"http://instagram.com/"', ref: 'group.instagram'} .group{"ng-show" => "groups.length == 0"} .row.pad-top From f8153c07b16ac3c476484a9206e6c7010f4e1876 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Sun, 22 Feb 2015 16:20:11 +1100 Subject: [PATCH 080/384] moving email from "web" to "contact" on group edit page --- app/views/admin/enterprise_groups/_form_address.html.haml | 5 +++++ app/views/admin/enterprise_groups/_form_web.html.haml | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/admin/enterprise_groups/_form_address.html.haml b/app/views/admin/enterprise_groups/_form_address.html.haml index 081af7110e..12bb7ef315 100644 --- a/app/views/admin/enterprise_groups/_form_address.html.haml +++ b/app/views/admin/enterprise_groups/_form_address.html.haml @@ -6,6 +6,11 @@ = af.label :phone .omega.eight.columns = af.text_field :phone, { placeholder: "eg. 98 7654 3210"} + .row + .alpha.three.columns + = f.label :email + .omega.eight.columns + = f.text_field :email .row .three.columns.alpha = af.label :address1 diff --git a/app/views/admin/enterprise_groups/_form_web.html.haml b/app/views/admin/enterprise_groups/_form_web.html.haml index ea985259c3..42638d94c6 100644 --- a/app/views/admin/enterprise_groups/_form_web.html.haml +++ b/app/views/admin/enterprise_groups/_form_web.html.haml @@ -5,11 +5,6 @@ = f.label :website .omega.eight.columns = f.text_field :website, { placeholder: "eg. www.truffles.com"} - .row - .alpha.three.columns - = f.label :email - .omega.eight.columns - = f.text_field :email .row .alpha.three.columns = f.label :facebook, 'Facebook' From fdde55f6319d26984bd8f86d5f247f4e100aee51 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 23 Feb 2015 10:03:03 +1100 Subject: [PATCH 081/384] Add spec for ext_url filter, refactor --- .../darkswarm/filters/ext_url.js.coffee | 10 ++++------ .../darkswarm/filters/ext_url_spec.js.coffee | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 spec/javascripts/unit/darkswarm/filters/ext_url_spec.js.coffee diff --git a/app/assets/javascripts/darkswarm/filters/ext_url.js.coffee b/app/assets/javascripts/darkswarm/filters/ext_url.js.coffee index 8d4b8b90fe..38eed7c294 100644 --- a/app/assets/javascripts/darkswarm/filters/ext_url.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/ext_url.js.coffee @@ -1,9 +1,7 @@ -Darkswarm.filter "ext_url", () -> +Darkswarm.filter "ext_url", -> urlPattern = /^https?:\/\// (url, prefix) -> - if (!url) - return url - if (url.match(urlPattern)) - return url + if !url || url.match(urlPattern) + url else - return prefix + url + prefix + url diff --git a/spec/javascripts/unit/darkswarm/filters/ext_url_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/ext_url_spec.js.coffee new file mode 100644 index 0000000000..1575d8c246 --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/ext_url_spec.js.coffee @@ -0,0 +1,19 @@ +describe "ensuring absolute URL", -> + filter = null + + beforeEach -> + module 'Darkswarm' + inject ($filter) -> + filter = $filter 'ext_url' + + it "returns null when no URL given", -> + expect(filter(null, "http://")).toBeNull() + + it "returns the URL as-is for http URLs", -> + expect(filter("http://example.com", "http://")).toEqual "http://example.com" + + it "returns the URL as-is for https URLs", -> + expect(filter("https://example.com", "https://")).toEqual "https://example.com" + + it "returns with URL with prefix added when a relative URL is given", -> + expect(filter("example.com", "http://")).toEqual "http://example.com" From a3df4bf0262154d8dfa3667f6e20d09fc8dca77c Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 24 Feb 2015 13:43:00 +1000 Subject: [PATCH 082/384] LD: Adding additional functionality for order cycle management reports - adding hub code model, adding temp_controlled bool, adding delivery report --- .../admin/reports_controller_decorator.rb | 5 +- app/models/customer.rb | 2 + db/migrate/20150122145607_create_customers.rb | 13 ++++ ...controlled_to_spree_shipping_categories.rb | 5 ++ db/schema.rb | 20 ++++- .../order_cycle_management_report.rb | 78 +++++++++++++++---- spec/features/admin/reports_spec.rb | 15 +++- 7 files changed, 114 insertions(+), 24 deletions(-) create mode 100644 app/models/customer.rb create mode 100644 db/migrate/20150122145607_create_customers.rb create mode 100644 db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 655d1778dc..50954950e0 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -27,7 +27,8 @@ Spree::Admin::ReportsController.class_eval do ["Addresses", :addresses] ], order_cycle_management: [ - ["Payment Methods Report", :payment_methods_report] + ["Payment Methods Report", :payment_methods], + ["Delivery Report", :delivery] ] } @@ -58,7 +59,6 @@ Spree::Admin::ReportsController.class_eval do @report_types = REPORT_TYPES[:customers] @report_type = params[:report_type] @report = OpenFoodNetwork::CustomersReport.new spree_current_user, params - render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv") end @@ -68,7 +68,6 @@ Spree::Admin::ReportsController.class_eval do @report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user, params @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) - @orders = @search.result render_report(@report.header, @report.table, params[:csv], "order_cycle_management_#{timestamp}.csv") diff --git a/app/models/customer.rb b/app/models/customer.rb new file mode 100644 index 0000000000..047dcb0c04 --- /dev/null +++ b/app/models/customer.rb @@ -0,0 +1,2 @@ +class Customer < ActiveRecord::Base +end diff --git a/db/migrate/20150122145607_create_customers.rb b/db/migrate/20150122145607_create_customers.rb new file mode 100644 index 0000000000..7993f9a4b6 --- /dev/null +++ b/db/migrate/20150122145607_create_customers.rb @@ -0,0 +1,13 @@ +class CreateCustomers < ActiveRecord::Migration + def change + create_table :customers do |t| + t.string :email + t.references :enterprise + t.string :code + + t.timestamps + end + add_index :customers, [:enterprise_id, :code], unique: true + add_index :customers, :email + end +end diff --git a/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb new file mode 100644 index 0000000000..d939d5fdf3 --- /dev/null +++ b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb @@ -0,0 +1,5 @@ +class AddTemperatureControlledToSpreeShippingCategories < ActiveRecord::Migration + def change + add_column :spree_shipping_categories, :temperature_controlled, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 2d8f28fbdd..3c77da926a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20141219034321) do +ActiveRecord::Schema.define(:version => 20150216075336) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -155,6 +155,17 @@ ActiveRecord::Schema.define(:version => 20141219034321) do add_index "coordinator_fees", ["enterprise_fee_id"], :name => "index_coordinator_fees_on_enterprise_fee_id" add_index "coordinator_fees", ["order_cycle_id"], :name => "index_coordinator_fees_on_order_cycle_id" + create_table "customers", :force => true do |t| + t.string "email" + t.integer "enterprise_id" + t.string "code" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "customers", ["email"], :name => "index_customers_on_email" + add_index "customers", ["enterprise_id", "code"], :name => "index_customers_on_enterprise_id_and_code", :unique => true + create_table "distributors_payment_methods", :id => false, :force => true do |t| t.integer "distributor_id" t.integer "payment_method_id" @@ -574,9 +585,9 @@ ActiveRecord::Schema.define(:version => 20141219034321) do t.string "email" t.text "special_instructions" t.integer "distributor_id" - t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" + t.integer "order_cycle_id" t.integer "cart_id" end @@ -837,8 +848,9 @@ ActiveRecord::Schema.define(:version => 20141219034321) do create_table "spree_shipping_categories", :force => true do |t| t.string "name" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "temperature_controlled" end create_table "spree_shipping_methods", :force => true do |t| diff --git a/lib/open_food_network/order_cycle_management_report.rb b/lib/open_food_network/order_cycle_management_report.rb index 06a90bfe25..ac98b062c4 100644 --- a/lib/open_food_network/order_cycle_management_report.rb +++ b/lib/open_food_network/order_cycle_management_report.rb @@ -1,3 +1,5 @@ +require 'open_food_network/user_balance_calculator' + module OpenFoodNetwork class OrderCycleManagementReport attr_reader :params @@ -7,23 +9,19 @@ module OpenFoodNetwork end def header - ["First Name", "Last Name", "Email", "Phone", "Hub", "Shipping Method", "Payment Method", "Amount"] + if is_payment_methods? + ["First Name", "Last Name", "Hub", "Hub Code", "Email", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance"] + else + ["First Name", "Last Name", "Hub", "Hub Code", "Delivery Address", "Delivery Postcode", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance", "Temp Controlled Items?", "Special Instructions"] + end end def table - orders.map do |order| - ba = order.billing_address - da = order.distributor.andand.address - [ba.firstname, - ba.lastname, - order.email, - ba.phone, - order.distributor.andand.name, - order.shipping_method.andand.name, - order.payments.first.andand.payment_method.andand.name, - order.payments.first.amount - ] - end + if is_payment_methods? + orders.map { |o| payment_method_row o } + else + orders.map { |o| delivery_row o } + end end def orders @@ -34,6 +32,44 @@ module OpenFoodNetwork filter_to_order_cycle filter_to_payment_method filter_to_shipping_method orders end + + private + + def payment_method_row (order) + ba = order.billing_address + da = order.distributor.andand.address + [ba.firstname, + ba.lastname, + order.distributor.andand.name, + customer_code(order.email), + order.email, + ba.phone, + order.shipping_method.andand.name, + order.payments.first.andand.payment_method.andand.name, + order.payments.first.amount, + OpenFoodNetwork::UserBalanceCalculator.new(order.user, order.distributor).balance + ] + end + + def delivery_row (order) + ba = order.billing_address + da = order.distributor.andand.address + [ba.firstname, + ba.lastname, + order.distributor.andand.name, + customer_code(order.email), + "#{ba.address1} #{ba.address2} #{ba.city}", + ba.zipcode, + ba.phone, + order.shipping_method.andand.name, + order.payments.first.andand.payment_method.andand.name, + order.payments.first.amount, + OpenFoodNetwork::UserBalanceCalculator.new(order.user, order.distributor).balance, + has_temperature_controlled_items?(order), + order.special_instructions + ] + end + def filter_to_payment_method(orders) if params[:payment_method_name].present? orders.with_payment_method_name(params[:payment_method_name]) @@ -57,5 +93,19 @@ module OpenFoodNetwork orders end end + + def has_temperature_controlled_items?(order) + order.line_items.any? { |line_item| line_item.product.shipping_category.nil? ? + false : line_item.product.shipping_category.temperature_controlled? } + end + + def is_payment_methods? + params[:report_type] == "payment_methods" + end + + def customer_code (email) + customer = Customer.where(email: email).first + customer.nil? ? "" : customer.code + end end end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 0524a6769e..0956f268d3 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -65,12 +65,21 @@ feature %q{ click_link "Reports" end - scenario "order payment method report" do - click_link "Order Cycle Management" + scenario "payment method report" do + click_link "Payment Methods Report" rows = find("table#listing_order_payment_methods").all("thead tr") table = rows.map { |r| r.all("th").map { |c| c.text.strip } } table.sort.should == [ - ["First Name", "Last Name", "Email", "Phone", "Hub", "Shipping Method", "Payment Method", "Amount"] + ["First Name", "Last Name", "Hub", "Hub Code", "Email", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance"] + ].sort + end + + scenario "delivery report" do + click_link "Delivery Report" + rows = find("table#listing_order_payment_methods").all("thead tr") + table = rows.map { |r| r.all("th").map { |c| c.text.strip } } + table.sort.should == [ + ["First Name", "Last Name", "Hub", "Hub Code", "Delivery Address", "Delivery Postcode", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance", "Temp Controlled Items?", "Special Instructions"] ].sort end end From 9daf7e395597b93a35a1e7265b35b2a239e5f12b Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 25 Feb 2015 09:38:07 +1100 Subject: [PATCH 083/384] Removing spans on follow icons as these show up as blank spaces when no data to fill. --- app/views/groups/_contact.html.haml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index 8b45ca8437..915c608d15 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -19,19 +19,15 @@ - if @group.facebook.present? || @group.twitter.present? || @group.linkedin.present? || @group.instagram.present? %div.modal-centered.pad-top %p.modal-header Follow - .follow-icons{bindonce: true} - %span - =link_to_service "http://twitter.com/", @group.twitter do - %i.ofn-i_041-twitter - %span - =link_to_service "https://www.facebook.com/", @group.facebook do - %i.ofn-i_044-facebook - %span - =link_to_service "https://www.linkedin.com/in/", @group.linkedin do - %i.ofn-i_042-linkedin - %span - =link_to_service "http://instagram.com/", @group.instagram do - %i.ofn-i_043-instagram + .follow-icons{bindonce: true} + =link_to_service "http://twitter.com/", @group.twitter do + %i.ofn-i_041-twitter + =link_to_service "https://www.facebook.com/", @group.facebook do + %i.ofn-i_044-facebook + =link_to_service "https://www.linkedin.com/in/", @group.linkedin do + %i.ofn-i_042-linkedin + =link_to_service "http://instagram.com/", @group.instagram do + %i.ofn-i_043-instagram %div{bindonce: true} - if @group.address1.present? || @group.city.present? From 122cf6c0659d6bb2e7573b0897ec51d8820f6804 Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 25 Feb 2015 10:22:33 +1100 Subject: [PATCH 084/384] Groups index page styling for responsive use cases. --- .../stylesheets/darkswarm/groups.css.sass | 17 ++++++++++- app/views/groups/index.html.haml | 29 +++++++++---------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 039678de77..7f70bf7211 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -13,12 +13,27 @@ &:hover, &:focus, &:active text-decoration: underline + .groups-icons + text-align: right + a + font-size: 1.5em + + .groups-header + border: 2px solid $clr-brick-light-bright + @include border-radius-mixed(0.5em, 0.5em, 0, 0) + margin: -1rem 0 1rem + padding: 1rem 0.9375rem + @media screen and (min-width: 640px) + border: 0 none + @include border-radius(0) + margin: 0 + padding: 0 + .group padding-bottom: 0.5em .row div font-size: 110% .row a - font-weight: 500 vertical-align: middle .ofn-i_035-groups font-size: 120% diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 6115410b43..5865823cd9 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -8,30 +8,27 @@ .small-12.columns %h1 Groups / regions / TODO: Maikel this search input still doesn't work. - / %p - / %input.animate-show{type: :text, - / "ng-model" => "query", - / placeholder: "Search name or keyword", - / "ng-debounce" => "150", - / "ofn-disable-enter" => true} + %p + %input.animate-show{type: :text, + "ng-model" => "query", + placeholder: "Search name or keyword", + "ng-debounce" => "150", + "ofn-disable-enter" => true} .group{"ng-repeat" => "group in groups = (Groups.groups | groups:query | orderBy:order)", name: "group{{group.id}}", id: "group{{group.id}}"} .row.pad-top{bindonce: true} .small-12.medium-6.columns - %a{"ng-href" => "groups/{{group.id}}"} - %i.ofn-i_035-groups - %span.group-name - {{ group.name }} - / %br - / %small - / %em - / {{ group.description }} - .small-4.medium-2.columns + .groups-header + %a{"ng-href" => "groups/{{group.id}}"} + %i.ofn-i_035-groups + %span.group-name + {{ group.name }} + .small-3.medium-2.columns %p {{ group.state }} - .small-8.medium-4.columns.text-right + .small-9.medium-4.columns.groups-icons %p %link-to-service.ofn-i_050-mail-circle{service: '""', ref: 'group.email.split("").reverse().join("")', mailto: true} %link-to-service.ofn-i_049-web{service: '"http://"', ref: 'group.website'} From 1f7aec0c1d7f56d292a5ff3ba38b8595ffd75d43 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 26 Feb 2015 13:09:46 +1100 Subject: [PATCH 085/384] group owner select box in own tab --- .../controllers/side_menu_controller.js.coffee | 1 + app/views/admin/enterprise_groups/_form.html.haml | 1 + .../_form_primary_details.html.haml | 9 --------- .../admin/enterprise_groups/_form_users.html.haml | 14 ++++++++++++++ 4 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 app/views/admin/enterprise_groups/_form_users.html.haml diff --git a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee index 63f05a9376..7b9a8165a1 100644 --- a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee @@ -5,6 +5,7 @@ angular.module("admin.enterprise_groups") $scope.menu.setItems [ { name: 'Primary Details', icon_class: "icon-user" } + { name: 'Users', icon_class: "icon-user" } { name: 'About', icon_class: "icon-pencil" } { name: 'Images', icon_class: "icon-picture" } { name: 'Contact', icon_class: "icon-phone" } diff --git a/app/views/admin/enterprise_groups/_form.html.haml b/app/views/admin/enterprise_groups/_form.html.haml index 1b96909d3e..39a4b8bd1e 100644 --- a/app/views/admin/enterprise_groups/_form.html.haml +++ b/app/views/admin/enterprise_groups/_form.html.haml @@ -8,6 +8,7 @@ .one.column   .eleven.columns.omega.fullwidth_inputs = render 'form_primary_details', f: f + = render 'form_users', f: f = render 'form_about', f: f = render 'form_images', f: f = render 'form_address', f: f diff --git a/app/views/admin/enterprise_groups/_form_primary_details.html.haml b/app/views/admin/enterprise_groups/_form_primary_details.html.haml index 27dec0701c..6d326e33fa 100644 --- a/app/views/admin/enterprise_groups/_form_primary_details.html.haml +++ b/app/views/admin/enterprise_groups/_form_primary_details.html.haml @@ -10,15 +10,6 @@ %br/ = f.text_field :description - - if spree_current_user.admin? - .row - .three.columns.alpha - =f.label :owner_id, 'Owner' - .with-tip{'data-powertip' => "The primary user responsible for this group."} - %a What's this? - .eight.columns.omega - = f.hidden_field :owner_id, class: "select2 fullwidth", 'ofn-user-autocomplete' => true, email: @owner_email - = f.field_container :on_front_page do = f.label :on_front_page, 'On front page?' %br/ diff --git a/app/views/admin/enterprise_groups/_form_users.html.haml b/app/views/admin/enterprise_groups/_form_users.html.haml new file mode 100644 index 0000000000..0a8a5dd635 --- /dev/null +++ b/app/views/admin/enterprise_groups/_form_users.html.haml @@ -0,0 +1,14 @@ +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Users'" } } + %legend Users + .row + .three.columns.alpha + =f.label :owner_id, 'Owner' + .with-tip{'data-powertip' => "The primary user responsible for this group."} + %a What's this? + .eight.columns.omega + - if spree_current_user.admin? + = f.hidden_field :owner_id, + class: "select2 fullwidth", + 'user-select' => "{id:'#{@enterprise_group.owner.andand.id}', email:'#{@enterprise_group.owner.andand.email}'}" + - else + = @enterprise_group.owner.andand.email From fa4741eb658cb4656d62eef127e62dcb02fd4614 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 26 Feb 2015 13:47:40 +1100 Subject: [PATCH 086/384] Update auto-creation of E2E links: more specific and restricted link creation --- app/models/enterprise.rb | 31 ++++++++---- spec/models/enterprise_spec.rb | 86 ++++++++++++++++++++++------------ 2 files changed, 76 insertions(+), 41 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 8f5c940e77..462910aa0d 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -253,6 +253,10 @@ class Enterprise < ActiveRecord::Base self.sells != "none" end + def is_hub + self.sells == 'any' + end + # Simplify enterprise categories for frontend logic and icons, and maybe other things. def category # Make this crazy logic human readable so we can argue about it sanely. @@ -366,26 +370,33 @@ class Enterprise < ActiveRecord::Base end def relate_to_owners_enterprises - # When a new enterprise is created, we relate them to all enterprises owned by - # the same owner, in both directions. So all enterprises owned by the same owner - # will have permissions to every other one, in both directions. + # When a new producer is created, it grants permissions to all pre-existing hubs + # When a new hub is created, + # - it grants permissions to all pre-existing hubs + # - all producers grant permission to it enterprises = owner.owned_enterprises.where('enterprises.id != ?', self) - enterprises.each do |enterprise| + # We grant permissions to all pre-existing hubs + enterprises.is_hub.each do |enterprise| EnterpriseRelationship.create!(parent: self, child: enterprise, permissions_list: [:add_to_order_cycle, :manage_products, :edit_profile, :create_variant_overrides]) + end - EnterpriseRelationship.create!(parent: enterprise, - child: self, - permissions_list: [:add_to_order_cycle, - :manage_products, - :edit_profile, - :create_variant_overrides]) + # All pre-existing producers grant permission to new hubs + if is_hub + enterprises.is_primary_producer.each do |enterprise| + EnterpriseRelationship.create!(parent: enterprise, + child: self, + permissions_list: [:add_to_order_cycle, + :manage_products, + :edit_profile, + :create_variant_overrides]) + end end end diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 494a543260..365bfd6ab0 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -594,46 +594,70 @@ describe Enterprise do describe "callbacks" do describe "after creation" do let(:owner) { create(:user, enterprise_limit: 10) } - let(:hub1) { create(:distributor_enterprise, owner: owner) } let(:hub2) { create(:distributor_enterprise, owner: owner) } - let(:producer) { create(:supplier_enterprise, owner: owner) } + let(:hub3) { create(:distributor_enterprise, owner: owner) } + let(:producer1) { create(:supplier_enterprise, owner: owner) } + let(:producer2) { create(:supplier_enterprise, owner: owner) } - let(:er1) { EnterpriseRelationship.where(child_id: hub1).last } - let(:er2) { EnterpriseRelationship.where(child_id: hub2).last } - let(:er3) { EnterpriseRelationship.where(child_id: producer).last } - let(:er4) { EnterpriseRelationship.where(parent_id: hub1).last } - let(:er5) { EnterpriseRelationship.where(parent_id: hub2).last } - let(:er6) { EnterpriseRelationship.where(parent_id: producer).last } - - it "establishes bi-directional relationships for new hubs with the owner's hubs and producers" do - hub1 - hub2 - producer - enterprise = nil - - expect do - enterprise = create(:enterprise, owner: owner) - end.to change(EnterpriseRelationship, :count).by(6) - - [er1, er2, er3].each do |er| - er.parent.should == enterprise - er.permissions.map(&:name).sort.should == ['add_to_order_cycle', 'manage_products', 'edit_profile', 'create_variant_overrides'].sort + describe "when a producer is created" do + before do + hub1 + hub2 end - [er4, er5, er6].each do |er| - er.child.should == enterprise - er.permissions.map(&:name).sort.should == ['add_to_order_cycle', 'manage_products', 'edit_profile', 'create_variant_overrides'].sort + it "creates links from the new producer to all hubs owned by the same user, granting all permissions" do + producer1 + + should_have_enterprise_relationship from: producer1, to: hub1, with: :all_permissions + should_have_enterprise_relationship from: producer1, to: hub2, with: :all_permissions + end + + it "does not create any other links" do + expect do + producer1 + end.to change(EnterpriseRelationship, :count).by(2) end end - it "establishes bi-directional relationships when producers are created" do - hub1 - hub2 - expect do - producer - end.to change(EnterpriseRelationship, :count).by(4) + describe "when a new hub is created" do + it "it creates links to the hub, from all producers owned by the same user, granting all permissions" do + producer1 + producer2 + hub1 + + should_have_enterprise_relationship from: producer1, to: hub1, with: :all_permissions + should_have_enterprise_relationship from: producer2, to: hub1, with: :all_permissions + end + + + it "creates links from the new hub to all hubs owned by the same user, granting all permissions" do + hub1 + hub2 + hub3 + + should_have_enterprise_relationship from: hub2, to: hub1, with: :all_permissions + should_have_enterprise_relationship from: hub3, to: hub1, with: :all_permissions + should_have_enterprise_relationship from: hub3, to: hub2, with: :all_permissions + end + + it "does not create any other links" do + producer1 + producer2 + expect { hub1 }.to change(EnterpriseRelationship, :count).by(2) # 2 producer links + expect { hub2 }.to change(EnterpriseRelationship, :count).by(3) # 2 producer links + 1 hub link + expect { hub3 }.to change(EnterpriseRelationship, :count).by(4) # 2 producer links + 2 hub links + end + end + + + def should_have_enterprise_relationship(opts={}) + er = EnterpriseRelationship.where(parent_id: opts[:from], child_id: opts[:to]).last + er.should_not be_nil + if opts[:with] == :all_permissions + er.permissions.map(&:name).sort.should == ['add_to_order_cycle', 'manage_products', 'edit_profile', 'create_variant_overrides'].sort + end end end end From 379b702b9be8da06b799531b74d8c37eb0f9c11e Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 26 Feb 2015 16:22:15 +1100 Subject: [PATCH 087/384] spec: testing array of owned groups without order --- spec/models/spree/user_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index 8510b89e0e..39692c2029 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -33,8 +33,8 @@ describe Spree.user_class do let!(:g3) { create(:enterprise_group, owner: u2) } it "provides access to owned groups" do - expect(u1.owned_groups(:reload)).to eq [g1, g2] - expect(u2.owned_groups(:reload)).to eq [g3] + expect(u1.owned_groups(:reload)).to match_array([g1, g2]) + expect(u2.owned_groups(:reload)).to match_array([g3]) end end From ff4bd449a23af142fd54ad4092a69548f103df03 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 26 Feb 2015 16:25:18 +1100 Subject: [PATCH 088/384] Setting default_country_id by application.yml The spree default_country_id was set using ENV["DEFAULT_COUNTRY"] for production environment, but not for tests and development. Since tests reset the default_country_id in specs/support/seeds.rb, only the development environment had a fix id set to 12. This is removed now. This fixes creating enterprises and enterprise groups without sample data (12 is Australia). --- config/initializers/spree.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/config/initializers/spree.rb b/config/initializers/spree.rb index dca89955b7..5912e0b9ec 100644 --- a/config/initializers/spree.rb +++ b/config/initializers/spree.rb @@ -14,13 +14,8 @@ Spree.config do |config| config.checkout_zone = ENV["CHECKOUT_ZONE"] config.address_requires_state = true - # 12 should be Australia. Hardcoded for CI (Jenkins), where countries are not pre-loaded. - if Rails.env.test? or Rails.env.development? - config.default_country_id = 12 - else - country = Spree::Country.find_by_name(ENV["DEFAULT_COUNTRY"]) - config.default_country_id = country.id if country.present? - end + country = Spree::Country.find_by_name(ENV["DEFAULT_COUNTRY"]) + config.default_country_id = country.id if country.present? # -- spree_paypal_express # Auto-capture payments. Without this option, payments must be manually captured in the paypal interface. From 69fd3f0b602715ecabce35b768fc34226424c150 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 26 Feb 2015 16:41:51 +1100 Subject: [PATCH 089/384] Fix link to group pages if someone enters "/groups/" instead of "/group" --- app/views/groups/index.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 5865823cd9..262e188e3c 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -7,7 +7,6 @@ #active-table-search.row.pad-top .small-12.columns %h1 Groups / regions - / TODO: Maikel this search input still doesn't work. %p %input.animate-show{type: :text, "ng-model" => "query", @@ -21,7 +20,7 @@ .row.pad-top{bindonce: true} .small-12.medium-6.columns .groups-header - %a{"ng-href" => "groups/{{group.id}}"} + %a{"ng-href" => "/groups/{{group.id}}"} %i.ofn-i_035-groups %span.group-name {{ group.name }} From 2ba632456f083c818438577d5572c24b177cff8c Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 27 Feb 2015 12:40:13 +1100 Subject: [PATCH 090/384] WIP email mailer updates for customer email confirmation etc. --- .../images/open-food-network-beta-black.png | Bin 0 -> 18863 bytes app/views/layouts/mailer.html.haml | 6 +- .../confirm_email_for_customer.html.haml | 100 ++++++++++-------- 3 files changed, 58 insertions(+), 48 deletions(-) create mode 100644 app/assets/images/open-food-network-beta-black.png diff --git a/app/assets/images/open-food-network-beta-black.png b/app/assets/images/open-food-network-beta-black.png new file mode 100644 index 0000000000000000000000000000000000000000..8e5f810023c5ab5094124d44622f9dc7116a4057 GIT binary patch literal 18863 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 UI#+Y7x&QzG07*qoM6N<$g6n^ahX4Qo literal 0 HcmV?d00001 diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml index ef8d4e2ec1..9042c91213 100644 --- a/app/views/layouts/mailer.html.haml +++ b/app/views/layouts/mailer.html.haml @@ -7,15 +7,15 @@ Open Food Network = stylesheet_link_tag 'mail/all' %body{:bgcolor => "#FFFFFF" } - %table.head-wrap{:bgcolor => "#333333"} + %table.head-wrap{:bgcolor => "#f2f2f2"} %tr %td %td.header.container .content - %table{:bgcolor => "#333333"} + %table{:bgcolor => "#f2f2f2"} %tr %td - %img{:src => "#{ asset_path 'open-food-network-beta.png' }", :width => "200"}/ + %img{:src => "#{ asset_path 'open-food-network-beta-black.png' }", :width => "200", :height => "49"}/ %td{:align => "right"} %h6.collapse Open Food Network diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 69629e256b..9afb95f298 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -1,25 +1,31 @@ -%h3 Hi #{@order.bill_address.firstname}, %table.social.white-bg{:width => "100%"} %tr %td %table.column{:align => "left"} %tr %td - %p - %strong Order confirmation ##{@order.number} - %p - Thanks for shopping on - %strong= "#{Spree::Config.site_name}." - Here are the details for your order from - %strong= "#{@order.distributor.name}." + %h3 + Hi #{@order.bill_address.firstname}, + %br + %br + Thanks for shopping at + %strong= "#{@order.distributor.name}" + ! %table.column{:align => "left"} %tr %td - %img.float-right{:src => "#{@order.distributor.logo.url(:thumb)}"}/ + %img.float-right{:src => "#{@order.distributor.logo.url(:small)}"}/ %span.clear %p   - - +%h4 + Order confirmation + %strong ##{@order.number} +%p + / Thanks for shopping on + / %strong= "#{Spree::Config.site_name}." + Here are the details for your order from + %strong= "#{@order.distributor.name}:" +%p   %p.callout %strong Order summary %table.order-summary{:width => "100%"} @@ -33,9 +39,11 @@ - @order.line_items.each do |item| %tr %td - = raw(item.variant.product.supplier.name) - %br = "#{raw(item.variant.product.name)} #{raw(item.variant.options_text)}" + %br + %small + %em= raw(item.variant.product.supplier.name) + %td{:align => "right"} = item.quantity %td{:align => "right"} @@ -76,7 +84,7 @@ Delivery details %p Your order will be delivered to: - %br + %p #{@order.ship_address.full_name} %br #{@order.ship_address.full_address} @@ -84,49 +92,49 @@ #{@order.ship_address.phone} - if @order.shipping_method.andand.description - %br - %br - #{@order.shipping_method.description.html_safe} + %p + #{@order.shipping_method.description.html_safe} - if @order.order_cycle.andand.pickup_time_for(@order.distributor) - %br - %br - Delivery on: #{@order.order_cycle.pickup_time_for(@order.distributor)} + %p + Delivery on: + %br + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - %br - %br - Other delivery information: #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - %br + %p + Other delivery information: + %br + %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - else / Collection details %p.callout %strong Collection details - %p - - if @order.shipping_method.andand.description - %br - %br - = @order.shipping_method.description.html_safe + + - if @order.shipping_method.andand.description + %p + @order.shipping_method.description.html_safe - - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + %p + Ready for collection: %br - %br - Ready for collection: #{@order.order_cycle.pickup_time_for(@order.distributor)} + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + %p + Collection instructions: %br - %br - Collection instructions: #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - if @order.special_instructions.present? - %br - %br - Notes: #{@order.special_instructions} - - %br + - if @order.special_instructions.present? + %p + %small + Customer notes: + %br + #{@order.special_instructions} -# Your order will be ready for collection on -# %strong{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} @@ -136,10 +144,12 @@ -# %a{:href => "https://goo.gl/maps/T1ArU", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #0096ad;", :target => "_blank"} -# google maps - %p.callout - #{@order.distributor.contact}, - %br/ + Kind regards, + %br + #{@order.distributor.contact} + %br + %br %strong= @order.distributor.name %br/ = @order.distributor.phone || "" From 87842ecaf5623825a6275b2a40dd659811864718 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 27 Feb 2015 14:00:49 +1100 Subject: [PATCH 091/384] More WIP confirmation email for customer and shopfront. fixing up the order summary table. overall design improvements. --- app/assets/stylesheets/mail/email.css.sass | 15 +++ .../confirm_email_for_customer.html.haml | 99 ++++++++--------- .../confirm_email_for_shop.html.haml | 105 ++++++++---------- 3 files changed, 111 insertions(+), 108 deletions(-) diff --git a/app/assets/stylesheets/mail/email.css.sass b/app/assets/stylesheets/mail/email.css.sass index bc425a1e38..38024a1cc4 100644 --- a/app/assets/stylesheets/mail/email.css.sass +++ b/app/assets/stylesheets/mail/email.css.sass @@ -56,6 +56,21 @@ table.social table.order-summary border-collapse: separate border-spacing: 0px 10px + tbody tr td + padding-left: 5px + padding-right: 5px + thead tr th + background-color: #f2f2f2 + border-bottom: 1px solid black + padding-left: 5px + padding-right: 5px + h4 + margin-top: 15px + tfoot + border-top: 1px solid black + tr td + padding-left: 5px + padding-right: 5px .social .soc-btn padding: 3px 7px diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 9afb95f298..166da19ec2 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -6,67 +6,62 @@ %td %h3 Hi #{@order.bill_address.firstname}, - %br - %br + %h4 Thanks for shopping at - %strong= "#{@order.distributor.name}" - ! + %strong= "#{@order.distributor.name}!" %table.column{:align => "left"} %tr - %td - %img.float-right{:src => "#{@order.distributor.logo.url(:small)}"}/ + %td{:align => "right"} + %img.float-right{:src => "#{@order.distributor.logo.url(:medium)}"}/ %span.clear + %p   %h4 Order confirmation %strong ##{@order.number} %p - / Thanks for shopping on - / %strong= "#{Spree::Config.site_name}." - Here are the details for your order from + Here are your order details from %strong= "#{@order.distributor.name}:" -%p   -%p.callout - %strong Order summary -%table.order-summary{:width => "100%"} - %tr - %td - %strong Item - %td{:align => "right", :width => "25%"} - %strong Qty - %td{:align => "right", :width => "25%"} - %strong Price - - @order.line_items.each do |item| - %tr - %td - = "#{raw(item.variant.product.name)} #{raw(item.variant.options_text)}" - %br - %small - %em= raw(item.variant.product.supplier.name) - - %td{:align => "right"} - = item.quantity - %td{:align => "right"} - = item.display_amount_with_adjustments - %tr - %td{:colspan => "3"}   - %tr - %td{:align => "right", :colspan => "2"} - Subtotal: - %td{:align => "right"} - = display_checkout_subtotal(@order) - - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| +%table.order-summary{:width => "100%"} + %thead + %tr + %th{:align => "left"} + %h4 Item + %th{:align => "right", :width => "25%"} + %h4 Qty + %th{:align => "right", :width => "25%"} + %h4 Price + %tbody + - @order.line_items.each do |item| + %tr + %td + %strong= "#{raw(item.variant.product.name)}" + = "#{raw(item.variant.options_text)}" + %br + %small + %em= raw(item.variant.product.supplier.name) + %td{:align => "right"} + = item.quantity + %td{:align => "right"} + = item.display_amount_with_adjustments + %tfoot %tr %td{:align => "right", :colspan => "2"} - = "#{raw(adjustment.label)}:" + Subtotal: %td{:align => "right"} - = adjustment.display_amount - %tr - %td{:align => "right", :colspan => "2"} - %strong Total: - %td{:align => "right"} - %strong= @order.display_total + = display_checkout_subtotal(@order) + - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| + %tr + %td{:align => "right", :colspan => "2"} + = "#{raw(adjustment.label)}:" + %td{:align => "right"} + = adjustment.display_amount + %tr + %td{:align => "right", :colspan => "2"} + %strong Total: + %td{:align => "right"} + %strong= @order.display_total %p   - if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description @@ -143,18 +138,18 @@ -# Pick-up your order at the rear of 34 Mason Street, Warragul. See it on -# %a{:href => "https://goo.gl/maps/T1ArU", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #0096ad;", :target => "_blank"} -# google maps - +%br %p.callout Kind regards, %br #{@order.distributor.contact} %br %br - %strong= @order.distributor.name - %br/ + = @order.distributor.name + %br = @order.distributor.phone || "" - %br/ + %br %a{:href => "mailto:#{@order.distributor.email}", :target => "_blank"} = @order.distributor.email - %br/ + %br = @order.distributor.website || "" \ No newline at end of file diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index 69629e256b..a7a30a35dc 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -1,64 +1,67 @@ -%h3 Hi #{@order.bill_address.firstname}, %table.social.white-bg{:width => "100%"} %tr %td %table.column{:align => "left"} %tr %td - %p - %strong Order confirmation ##{@order.number} - %p - Thanks for shopping on - %strong= "#{Spree::Config.site_name}." - Here are the details for your order from - %strong= "#{@order.distributor.name}." + %h3 + Hi #{@order.distributor.contact}, + %h4 + Well done! You have a new order for + %strong= "#{@order.distributor.name}!" %table.column{:align => "left"} %tr - %td - %img.float-right{:src => "#{@order.distributor.logo.url(:thumb)}"}/ + %td{:align => "right"} + %img.float-right{:src => "#{@order.distributor.logo.url(:medium)}"}/ %span.clear + %p   +%h4 + Order confirmation + %strong ##{@order.number} +%p + %strong= "#{@order.bill_address.firstname} #{@order.bill_address.lastname}" + completed the following order at your shopfront: - -%p.callout - %strong Order summary %table.order-summary{:width => "100%"} - %tr - %td - %strong Item - %td{:align => "right", :width => "25%"} - %strong Qty - %td{:align => "right", :width => "25%"} - %strong Price - - @order.line_items.each do |item| + %thead %tr - %td - = raw(item.variant.product.supplier.name) - %br - = "#{raw(item.variant.product.name)} #{raw(item.variant.options_text)}" - %td{:align => "right"} - = item.quantity - %td{:align => "right"} - = item.display_amount_with_adjustments - - %tr - %td{:colspan => "3"}   - %tr - %td{:align => "right", :colspan => "2"} - Subtotal: - %td{:align => "right"} - = display_checkout_subtotal(@order) - - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| + %th{:align => "left"} + %h4 Item + %th{:align => "right", :width => "25%"} + %h4 Qty + %th{:align => "right", :width => "25%"} + %h4 Price + %tbody + - @order.line_items.each do |item| + %tr + %td + %strong= "#{raw(item.variant.product.name)}" + = "#{raw(item.variant.options_text)}" + %br + %small + %em= raw(item.variant.product.supplier.name) + %td{:align => "right"} + = item.quantity + %td{:align => "right"} + = item.display_amount_with_adjustments + %tfoot %tr %td{:align => "right", :colspan => "2"} - = "#{raw(adjustment.label)}:" + Subtotal: %td{:align => "right"} - = adjustment.display_amount - %tr - %td{:align => "right", :colspan => "2"} - %strong Total: - %td{:align => "right"} - %strong= @order.display_total + = display_checkout_subtotal(@order) + - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| + %tr + %td{:align => "right", :colspan => "2"} + = "#{raw(adjustment.label)}:" + %td{:align => "right"} + = adjustment.display_amount + %tr + %td{:align => "right", :colspan => "2"} + %strong Total: + %td{:align => "right"} + %strong= @order.display_total %p   - if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description @@ -137,14 +140,4 @@ -# google maps -%p.callout - #{@order.distributor.contact}, - %br/ - %strong= @order.distributor.name - %br/ - = @order.distributor.phone || "" - %br/ - %a{:href => "mailto:#{@order.distributor.email}", :target => "_blank"} - = @order.distributor.email - %br/ - = @order.distributor.website || "" \ No newline at end of file += render 'shared/mailers/social_and_contact' From 60c246f1043bc12c80ae70c12f6a02338149e89a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 27 Feb 2015 15:21:23 +1100 Subject: [PATCH 092/384] fixing side menu ng-show of items --- app/views/admin/shared/_side_menu.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/shared/_side_menu.html.haml b/app/views/admin/shared/_side_menu.html.haml index 44efb2475c..cf1d3d42f7 100644 --- a/app/views/admin/shared/_side_menu.html.haml +++ b/app/views/admin/shared/_side_menu.html.haml @@ -2,7 +2,7 @@ %a.menu_item{ href: "", id: "{{ item.name.toLowerCase().replace(' ', '_') }}", ng: { repeat: '(index,item) in menu.items | filter:{visible:true}', click: 'select(index)', - show: 'showItem(item) !== false', + show: '!showItem || showItem(item)', class: '{ selected: item.selected }' } } %i{ class: "{{item.icon_class}}" } %span {{ item.name }} From 587e4ae86d6cdc65ce18d51684e116c9adf8de8a Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 27 Feb 2015 16:08:20 +1100 Subject: [PATCH 093/384] WIP getting all the confirmation details right for collection, delivery, etc. --- app/assets/stylesheets/mail/email.css.sass | 8 ++- .../confirm_email_for_customer.html.haml | 55 ++++++++++--------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/app/assets/stylesheets/mail/email.css.sass b/app/assets/stylesheets/mail/email.css.sass index 38024a1cc4..5f0a91bd72 100644 --- a/app/assets/stylesheets/mail/email.css.sass +++ b/app/assets/stylesheets/mail/email.css.sass @@ -67,7 +67,9 @@ table.order-summary h4 margin-top: 15px tfoot - border-top: 1px solid black + tr:first-child td + border-top: 1px solid black + padding-top: 5px tr td padding-left: 5px padding-right: 5px @@ -260,6 +262,10 @@ ul tr td padding: 15px +.pad + tr td + padding: 15px + .column-wrap padding: 0!important margin: 0 auto diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 166da19ec2..2125b7a5a8 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -66,19 +66,22 @@ - if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description %p.callout + %span{:style => "float:right;"} + PAID or NOT PAID %strong Payment summary - / /Heading Panel - / Payment Summary - %p= @order.payments.first.andand.payment_method.andand.description.andand.html_safe + %h4 + Paying via: + %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe + %p + %em= @order.payments.first.andand.payment_method.andand.description.andand.html_safe %p   - if @order.shipping_method.andand.require_ship_address - / Delivery details %p.callout %strong Delivery details - %p - Your order will be delivered to: + + %h4 Delivery address: %p #{@order.ship_address.full_name} %br @@ -91,15 +94,13 @@ #{@order.shipping_method.description.html_safe} - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + %h4 Delivery date: %p - Delivery on: - %br %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + %h5 Other delivery info: %p - Other delivery information: - %br %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - else @@ -113,31 +114,31 @@ @order.shipping_method.description.html_safe - if @order.order_cycle.andand.pickup_time_for(@order.distributor) - %p - Ready for collection: - %br + %h4 + Ready for collection on: %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + + - if @order.ship_address.full_address %p - Collection instructions: + %strong Collect from: %br - %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + #{@order.ship_address.full_address} + - if @order.shipping_method.andand.description + %br + %em #{@order.shipping_method.description.html_safe} + + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + %br + %em #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + - if @order.special_instructions.present? %p %small - Customer notes: + %strong Your notes / custom delivery instructions: %br #{@order.special_instructions} - -# Your order will be ready for collection on - -# %strong{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} - -# Tuesday 07 Dec - -# %p{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;margin-bottom: 10px;font-weight: normal;font-size: 14px;line-height: 1.6;"} - -# Pick-up your order at the rear of 34 Mason Street, Warragul. See it on - -# %a{:href => "https://goo.gl/maps/T1ArU", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #0096ad;", :target => "_blank"} - -# google maps %br %p.callout Kind regards, @@ -152,4 +153,6 @@ %a{:href => "mailto:#{@order.distributor.email}", :target => "_blank"} = @order.distributor.email %br - = @order.distributor.website || "" \ No newline at end of file + = @order.distributor.website || "" + + From 4eee86a2404dd3f0d444852e55dccac8552a1f28 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 27 Feb 2015 16:09:14 +1100 Subject: [PATCH 094/384] Tweak label --- .../spree/order_mailer/confirm_email_for_customer.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 2125b7a5a8..9f1be86c09 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -67,7 +67,7 @@ - if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description %p.callout %span{:style => "float:right;"} - PAID or NOT PAID + PAID / NOT PAID %strong Payment summary %h4 Paying via: From ba0d4c21112d62cc5f18fdb72e38a086ab253cb9 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 27 Feb 2015 16:55:36 +1100 Subject: [PATCH 095/384] More order confirmation email updates. Making language correct for hub vs user. --- .../confirm_email_for_customer.html.haml | 35 ++++-- .../confirm_email_for_shop.html.haml | 106 ++++++++++-------- 2 files changed, 82 insertions(+), 59 deletions(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 9f1be86c09..4ee414b614 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -67,7 +67,10 @@ - if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description %p.callout %span{:style => "float:right;"} - PAID / NOT PAID + - if @order.paid? + PAID + - else + NOT PAID %strong Payment summary %h4 Paying via: @@ -77,9 +80,19 @@ %p   - if @order.shipping_method.andand.require_ship_address + / Delivery details %p.callout %strong - Delivery details + - if @order.shipping_method.andand.name + #{@order.shipping_method.name.html_safe} + - else + Delivery details + + - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + %h4 + Delivery on: + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %br   %h4 Delivery address: %p @@ -90,16 +103,11 @@ #{@order.ship_address.phone} - if @order.shipping_method.andand.description - %p - #{@order.shipping_method.description.html_safe} - - - if @order.order_cycle.andand.pickup_time_for(@order.distributor) - %h4 Delivery date: - %p - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %p #{@order.shipping_method.description.html_safe} + %br   - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - %h5 Other delivery info: + %h4 Other delivery info: %p %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} @@ -107,7 +115,10 @@ / Collection details %p.callout %strong - Collection details + - if @order.shipping_method.andand.name + #{@order.shipping_method.name.html_safe} + - else + Collection details - if @order.shipping_method.andand.description %p @@ -115,7 +126,7 @@ - if @order.order_cycle.andand.pickup_time_for(@order.distributor) %h4 - Ready for collection on: + Ready for collection: %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - if @order.ship_address.full_address diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index a7a30a35dc..87ed2e7166 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -66,20 +66,36 @@ - if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description %p.callout + %span{:style => "float:right;"} + - if @order.paid? + PAID + - else + NOT PAID %strong Payment summary - / /Heading Panel - / Payment Summary - %p= @order.payments.first.andand.payment_method.andand.description.andand.html_safe + %h4 + Paying via: + %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe + %p + %em= @order.payments.first.andand.payment_method.andand.description.andand.html_safe %p   - if @order.shipping_method.andand.require_ship_address / Delivery details %p.callout %strong - Delivery details + - if @order.shipping_method.andand.name + #{@order.shipping_method.name.html_safe} + - else + Delivery details + + - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + %h4 + Delivery on: + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %br   + + %h4 Delivery address: %p - Your order will be delivered to: - %br #{@order.ship_address.full_name} %br #{@order.ship_address.full_address} @@ -87,57 +103,53 @@ #{@order.ship_address.phone} - if @order.shipping_method.andand.description - %br - %br - #{@order.shipping_method.description.html_safe} - - - if @order.order_cycle.andand.pickup_time_for(@order.distributor) - %br - %br - Delivery on: #{@order.order_cycle.pickup_time_for(@order.distributor)} + %p #{@order.shipping_method.description.html_safe} + %br   - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - %br - %br - Other delivery information: #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - %br + %h4 Other delivery info: + %p + %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - else / Collection details %p.callout %strong - Collection details - %p - - if @order.shipping_method.andand.description - %br - %br - = @order.shipping_method.description.html_safe + - if @order.shipping_method.andand.name + #{@order.shipping_method.name.html_safe} + - else + Collection details + + - if @order.shipping_method.andand.description + %p + @order.shipping_method.description.html_safe - - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + %h4 + Ready for collection: + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + + - if @order.ship_address.full_address + %p + %strong Collecting from: %br - %br - Ready for collection: #{@order.order_cycle.pickup_time_for(@order.distributor)} + #{@order.ship_address.full_address} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - %br - %br - Collection instructions: #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - - if @order.special_instructions.present? - %br - %br - Notes: #{@order.special_instructions} - - %br - - -# Your order will be ready for collection on - -# %strong{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} - -# Tuesday 07 Dec - -# %p{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;margin-bottom: 10px;font-weight: normal;font-size: 14px;line-height: 1.6;"} - -# Pick-up your order at the rear of 34 Mason Street, Warragul. See it on - -# %a{:href => "https://goo.gl/maps/T1ArU", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #0096ad;", :target => "_blank"} - -# google maps + - if @order.shipping_method.andand.description + %br + %em #{@order.shipping_method.description.html_safe} + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + %br + %em #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + + - if @order.special_instructions.present? + %p + %small + %strong Customer notes / custom delivery instructions: + %br + #{@order.special_instructions} +%p   += render 'shared/mailers/signoff' = render 'shared/mailers/social_and_contact' From 12bfb9301aa373363a63bed32920086052a5d5f4 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 27 Feb 2015 17:09:59 +1100 Subject: [PATCH 096/384] More updates dealing with pick up instructions. --- .../confirm_email_for_customer.html.haml | 36 ++++++++++--------- .../confirm_email_for_shop.html.haml | 34 +++++++++--------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 4ee414b614..83f0f66505 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -106,10 +106,10 @@ %p #{@order.shipping_method.description.html_safe} %br   - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - %h4 Other delivery info: - %p - %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + / - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + / %h4 Other delivery info: + / %p + / %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - else / Collection details @@ -131,24 +131,26 @@ - if @order.ship_address.full_address %p - %strong Collect from: + %strong Collecting from: %br #{@order.ship_address.full_address} - - if @order.shipping_method.andand.description - %br - %em #{@order.shipping_method.description.html_safe} + - if @order.shipping_method.andand.description + %br + %em #{@order.shipping_method.description.html_safe} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - %br - %em #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - - if @order.special_instructions.present? + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) %p - %small - %strong Your notes / custom delivery instructions: - %br - #{@order.special_instructions} + %strong Collection instructions: + %br + %em #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + +- if @order.special_instructions.present? + %p + %small + %strong Customer notes / custom delivery instructions: + %br + #{@order.special_instructions} %br %p.callout diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index 87ed2e7166..35c7593f45 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -106,10 +106,10 @@ %p #{@order.shipping_method.description.html_safe} %br   - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - %h4 Other delivery info: - %p - %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + / - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + / %h4 Other delivery info: + / %p + / %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - else / Collection details @@ -135,20 +135,22 @@ %br #{@order.ship_address.full_address} - - if @order.shipping_method.andand.description - %br - %em #{@order.shipping_method.description.html_safe} + - if @order.shipping_method.andand.description + %br + %em #{@order.shipping_method.description.html_safe} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - %br - %em #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - - if @order.special_instructions.present? + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) %p - %small - %strong Customer notes / custom delivery instructions: - %br - #{@order.special_instructions} + %strong Collection instructions: + %br + %em #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + +- if @order.special_instructions.present? + %p + %small + %strong Customer notes / custom delivery instructions: + %br + #{@order.special_instructions} %p   = render 'shared/mailers/signoff' From 2c2352cd4eea477cddad33fe879f58ca236e8304 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 27 Feb 2015 17:14:33 +1100 Subject: [PATCH 097/384] Final tweaks to different layout use cases for these emails. --- .../spree/order_mailer/confirm_email_for_customer.html.haml | 5 +++-- .../spree/order_mailer/confirm_email_for_shop.html.haml | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 83f0f66505..497649f844 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -142,10 +142,11 @@ - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) %p %strong Collection instructions: - %br - %em #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + %br + #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - if @order.special_instructions.present? + %br %p %small %strong Customer notes / custom delivery instructions: diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index 35c7593f45..c2dc363876 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -142,10 +142,11 @@ - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) %p %strong Collection instructions: - %br - %em #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + %br + #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - if @order.special_instructions.present? + %br %p %small %strong Customer notes / custom delivery instructions: From 53594b3a0f98d5528cf581874ee0d7770104232e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Feb 2015 13:03:27 +1100 Subject: [PATCH 098/384] Cannot add unconfirmed enterprises to order cycles --- app/helpers/order_cycles_helper.rb | 61 +++++++++++++------- app/views/admin/order_cycles/_form.html.haml | 5 +- spec/helpers/order_cycles_helper_spec.rb | 61 +++++++++++++++++--- 3 files changed, 97 insertions(+), 30 deletions(-) diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 3868de95c0..8eeda8b293 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -7,12 +7,24 @@ module OrderCyclesHelper OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises end - def order_cycle_producer_enterprises - order_cycle_permitted_enterprises.is_primary_producer.by_name + def order_cycle_producer_enterprises(options={}) + enterprises = order_cycle_permitted_enterprises.is_primary_producer.by_name + + if options[:without_validation] + enterprises + else + validated_enterprise_options enterprises, confirmed: true + end end - def order_cycle_coordinating_enterprises - order_cycle_permitted_enterprises.is_distributor.by_name + def order_cycle_coordinating_enterprises(options={}) + enterprises = order_cycle_permitted_enterprises.is_distributor.by_name + + if options[:without_validation] + enterprises + else + validated_enterprise_options enterprises, confirmed: true + end end def order_cycle_hub_enterprises(options={}) @@ -21,22 +33,7 @@ module OrderCyclesHelper if options[:without_validation] enterprises else - enterprises.map do |e| - disabled_message = nil - if e.shipping_methods.empty? && e.payment_methods.available.empty? - disabled_message = 'no shipping or payment methods' - elsif e.shipping_methods.empty? - disabled_message = 'no shipping methods' - elsif e.payment_methods.available.empty? - disabled_message = 'no payment methods' - end - - if disabled_message - ["#{e.name} (#{disabled_message})", e.id, {disabled: true}] - else - [e.name, e.id] - end - end + validated_enterprise_options enterprises, confirmed: true, shipping_and_payment_methods: true end end @@ -80,4 +77,28 @@ module OrderCyclesHelper order_cycle.exchanges.to_enterprises(current_distributor).outgoing.first.pickup_time end + private + + def validated_enterprise_options(enterprises, options={}) + enterprises.map do |e| + disabled_message = nil + if options[:shipping_and_payment_methods] && (e.shipping_methods.empty? || e.payment_methods.available.empty?) + if e.shipping_methods.empty? && e.payment_methods.available.empty? + disabled_message = 'no shipping or payment methods' + elsif e.shipping_methods.empty? + disabled_message = 'no shipping methods' + elsif e.payment_methods.available.empty? + disabled_message = 'no payment methods' + end + elsif options[:confirmed] && !e.confirmed? + disabled_message = 'unconfirmed' + end + + if disabled_message + ["#{e.name} (#{disabled_message})", e.id, {disabled: true}] + else + [e.name, e.id] + end + end + end end diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 9aaac195ee..4fa9d70578 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -15,13 +15,14 @@ %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_supplied_products_form' -= select_tag :new_supplier_id, options_from_collection_for_select(order_cycle_producer_enterprises, :id, :name), {'ng-model' => 'new_supplier_id'} += select_tag :new_supplier_id, options_for_select(order_cycle_producer_enterprises), {'ng-model' => 'new_supplier_id'} + = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' %h2 Coordinator = f.label :coordinator_id, 'Coordinator' -= f.collection_select :coordinator_id, order_cycle_coordinating_enterprises, :id, :name, {include_blank: true}, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} += f.select :coordinator_id, order_cycle_coordinating_enterprises, { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} = render 'coordinator_fees', f: f diff --git a/spec/helpers/order_cycles_helper_spec.rb b/spec/helpers/order_cycles_helper_spec.rb index c687463d59..3706c72579 100644 --- a/spec/helpers/order_cycles_helper_spec.rb +++ b/spec/helpers/order_cycles_helper_spec.rb @@ -1,7 +1,40 @@ require 'spec_helper' describe OrderCyclesHelper do + describe "finding producer enterprises" do + before do + helper.stub_chain(:order_cycle_permitted_enterprises, :is_primary_producer, :by_name) { "enterprise list" } + end + + it "asks for a validation option list" do + expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true}) + helper.order_cycle_producer_enterprises + end + end + + describe "finding coodinator enterprises" do + before do + helper.stub_chain(:order_cycle_permitted_enterprises, :is_distributor, :by_name) { "enterprise list" } + end + + it "asks for a validation option list" do + expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true}) + helper.order_cycle_coordinating_enterprises + end + end + describe "finding hub enterprises" do + before do + helper.stub_chain(:order_cycle_permitted_enterprises, :is_distributor, :by_name) { "enterprise list" } + end + + it "asks for a validation option list" do + expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true, shipping_and_payment_methods: true}) + helper.order_cycle_hub_enterprises + end + end + + describe "building a validated enterprise list" do let(:e) { create(:distributor_enterprise, name: 'enterprise') } before do @@ -10,22 +43,34 @@ describe OrderCyclesHelper do it "returns enterprises without shipping methods as disabled" do create(:payment_method, distributors: [e]) - helper.order_cycle_hub_enterprises.should == [['enterprise (no shipping methods)', e.id, {disabled: true}]] + expect(helper.send(:validated_enterprise_options, [e], shipping_and_payment_methods: true)) + .to eq [['enterprise (no shipping methods)', e.id, {disabled: true}]] end it "returns enterprises without payment methods as disabled" do create(:shipping_method, distributors: [e]) - helper.order_cycle_hub_enterprises.should == [['enterprise (no payment methods)', e.id, {disabled: true}]] + expect(helper.send(:validated_enterprise_options, [e], shipping_and_payment_methods: true)) + .to eq [['enterprise (no payment methods)', e.id, {disabled: true}]] end it "returns enterprises with unavailable payment methods as disabled" do create(:shipping_method, distributors: [e]) create(:payment_method, distributors: [e], active: false) - helper.order_cycle_hub_enterprises.should == [['enterprise (no payment methods)', e.id, {disabled: true}]] + expect(helper.send(:validated_enterprise_options, [e], shipping_and_payment_methods: true)) + .to eq [['enterprise (no payment methods)', e.id, {disabled: true}]] + end + + it "returns unconfirmed enterprises as disabled" do + create(:shipping_method, distributors: [e]) + create(:payment_method, distributors: [e]) + e.stub(:confirmed_at) { nil } + expect(helper.send(:validated_enterprise_options, [e], confirmed: true)) + .to eq [['enterprise (unconfirmed)', e.id, {disabled: true}]] end it "returns enterprises with neither shipping nor payment methods as disabled" do - helper.order_cycle_hub_enterprises.should == [['enterprise (no shipping or payment methods)', e.id, {disabled: true}]] + expect(helper.send(:validated_enterprise_options, [e], shipping_and_payment_methods: true)) + .to eq [['enterprise (no shipping or payment methods)', e.id, {disabled: true}]] end end @@ -33,8 +78,8 @@ describe OrderCyclesHelper do it "gives me the pickup time for the current order cycle" do d = create(:distributor_enterprise, name: 'Green Grass') oc1 = create(:simple_order_cycle, name: 'oc 1', distributors: [d]) - exchange = Exchange.find(oc1.exchanges.to_enterprises(d).outgoing.first.id) - exchange.update_attribute :pickup_time, "turtles" + exchange = Exchange.find(oc1.exchanges.to_enterprises(d).outgoing.first.id) + exchange.update_attribute :pickup_time, "turtles" helper.stub(:current_order_cycle).and_return oc1 helper.stub(:current_distributor).and_return d @@ -46,8 +91,8 @@ describe OrderCyclesHelper do oc1 = create(:simple_order_cycle, name: 'oc 1', distributors: [d]) oc2= create(:simple_order_cycle, name: 'oc 1', distributors: [d]) - exchange = Exchange.find(oc2.exchanges.to_enterprises(d).outgoing.first.id) - exchange.update_attribute :pickup_time, "turtles" + exchange = Exchange.find(oc2.exchanges.to_enterprises(d).outgoing.first.id) + exchange.update_attribute :pickup_time, "turtles" helper.stub(:current_order_cycle).and_return oc1 helper.stub(:current_distributor).and_return d From 8de6f983a218db40e1a05546cc5086521d5a718f Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Feb 2015 23:57:36 +1100 Subject: [PATCH 099/384] User must set the coordinator first when creating an order cycle --- .../admin/order_cycle.js.erb.coffee | 3 +- .../admin/order_cycles_controller.rb | 19 ++++++ app/helpers/admin/injection_helper.rb | 4 ++ app/helpers/order_cycles_helper.rb | 40 ++++++------- app/views/admin/order_cycles/_form.html.haml | 6 +- app/views/admin/order_cycles/new.html.haml | 1 + .../order_cycles/set_coordinator.html.haml | 15 +++++ .../admin/order_cycles_controller_spec.rb | 60 +++++++++++++++++++ spec/features/admin/order_cycles_spec.rb | 15 ++++- spec/helpers/order_cycles_helper_spec.rb | 18 +++--- 10 files changed, 143 insertions(+), 38 deletions(-) create mode 100644 app/views/admin/order_cycles/set_coordinator.html.haml create mode 100644 spec/controllers/admin/order_cycles_controller_spec.rb diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 608de85d8a..83f6e4b080 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -1,10 +1,11 @@ angular.module('admin.order_cycles', ['ngResource']) - .controller('AdminCreateOrderCycleCtrl', ['$scope', 'OrderCycle', 'Enterprise', 'EnterpriseFee', ($scope, OrderCycle, Enterprise, EnterpriseFee) -> + .controller('AdminCreateOrderCycleCtrl', ['$scope', 'OrderCycle', 'Enterprise', 'EnterpriseFee', 'ocInstance', ($scope, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> $scope.enterprises = Enterprise.index() $scope.supplied_products = Enterprise.supplied_products $scope.enterprise_fees = EnterpriseFee.index() $scope.order_cycle = OrderCycle.order_cycle + $scope.order_cycle.coordinator_id = ocInstance.coordinator_id $scope.loaded = -> Enterprise.loaded && EnterpriseFee.loaded diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 63daf918de..f978066bf4 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -6,6 +6,7 @@ module Admin include OrderCyclesHelper before_filter :load_order_cycle_set, :only => :index + before_filter :set_coordinator, only: :new def show respond_to do |format| @@ -85,5 +86,23 @@ module Admin def load_order_cycle_set @order_cycle_set = OrderCycleSet.new :collection => collection end + + def set_coordinator + if params[:coordinator_id] && @coordinator = order_cycle_coordinating_enterprises.find_by_id(params[:coordinator_id]) + return + end + + available_coordinators = order_cycle_coordinating_enterprises.select(&:confirmed?) + case available_coordinators.count + when 0 + flash[:error] = "None of your enterprises have permission to coordinate an order cycle" + redirect_to main_app.admin_order_cycles_path + when 1 + @coordinator = available_coordinators.first + else + flash[:error] = "You don't have permission to create an order cycle coordinated by that enterprise" if params[:coordinator_id] + render :set_coordinator + end + end end end diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 50b9aa1125..755ba1f5a3 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -62,6 +62,10 @@ module Admin admin_inject_json_ams_array "ofn.admin", "variantOverrides", @variant_overrides, Api::Admin::VariantOverrideSerializer end + def admin_inject_order_cycle_instance + render partial: "admin/json/injection_ams", locals: {ngModule: 'admin.order_cycles', name: 'ocInstance', json: "{coordinator_id: '#{@coordinator.id}'}"} + end + def admin_inject_spree_api_key render partial: "admin/json/injection_ams", locals: {ngModule: 'ofn.admin', name: 'SpreeApiKey', json: "'#{@spree_api_key.to_s}'"} end diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 8eeda8b293..66e6e16c20 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -7,34 +7,28 @@ module OrderCyclesHelper OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises end - def order_cycle_producer_enterprises(options={}) - enterprises = order_cycle_permitted_enterprises.is_primary_producer.by_name - - if options[:without_validation] - enterprises - else - validated_enterprise_options enterprises, confirmed: true - end + def order_cycle_producer_enterprises + order_cycle_permitted_enterprises.is_primary_producer.by_name end - def order_cycle_coordinating_enterprises(options={}) - enterprises = order_cycle_permitted_enterprises.is_distributor.by_name - - if options[:without_validation] - enterprises - else - validated_enterprise_options enterprises, confirmed: true - end + def order_cycle_producer_enterprise_options + validated_enterprise_options order_cycle_producer_enterprises, confirmed: true end - def order_cycle_hub_enterprises(options={}) - enterprises = order_cycle_permitted_enterprises.is_distributor.by_name + def order_cycle_coordinating_enterprises + order_cycle_permitted_enterprises.is_distributor.by_name + end - if options[:without_validation] - enterprises - else - validated_enterprise_options enterprises, confirmed: true, shipping_and_payment_methods: true - end + def order_cycle_coordinating_enterprise_options + validated_enterprise_options order_cycle_coordinating_enterprises, confirmed: true + end + + def order_cycle_hub_enterprises + order_cycle_permitted_enterprises.is_distributor.by_name + end + + def order_cycle_hub_enterprise_options + validated_enterprise_options order_cycle_hub_enterprises, confirmed: true, shipping_and_payment_methods: true end def order_cycle_status_class(order_cycle) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 4fa9d70578..b624bbd4b2 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -15,14 +15,14 @@ %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_supplied_products_form' -= select_tag :new_supplier_id, options_for_select(order_cycle_producer_enterprises), {'ng-model' => 'new_supplier_id'} += select_tag :new_supplier_id, options_for_select(order_cycle_producer_enterprise_options), {'ng-model' => 'new_supplier_id'} = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' %h2 Coordinator = f.label :coordinator_id, 'Coordinator' -= f.select :coordinator_id, order_cycle_coordinating_enterprises, { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} += f.select :coordinator_id, order_cycle_coordinating_enterprise_options, { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} = render 'coordinator_fees', f: f @@ -41,7 +41,7 @@ %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_distributed_products_form' -= select_tag :new_distributor_id, options_for_select(order_cycle_hub_enterprises), {'ng-model' => 'new_distributor_id'} += select_tag :new_distributor_id, options_for_select(order_cycle_hub_enterprise_options), {'ng-model' => 'new_distributor_id'} = f.submit 'Add distributor', 'ng-click' => 'addDistributor($event)' .actions diff --git a/app/views/admin/order_cycles/new.html.haml b/app/views/admin/order_cycles/new.html.haml index 817f790b19..95974f9ec5 100644 --- a/app/views/admin/order_cycles/new.html.haml +++ b/app/views/admin/order_cycles/new.html.haml @@ -1,6 +1,7 @@ %h1 New Order Cycle - ng_controller = order_cycles_simple_view ? 'AdminSimpleCreateOrderCycleCtrl' : 'AdminCreateOrderCycleCtrl' += admin_inject_order_cycle_instance = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.order_cycles', 'ng-controller' => ng_controller, 'ng-submit' => 'submit($event)'} do |f| - if order_cycles_simple_view diff --git a/app/views/admin/order_cycles/set_coordinator.html.haml b/app/views/admin/order_cycles/set_coordinator.html.haml new file mode 100644 index 0000000000..ebf0cffbb8 --- /dev/null +++ b/app/views/admin/order_cycles/set_coordinator.html.haml @@ -0,0 +1,15 @@ +%h4.text-center Select a coordinator for your order cycle + +%br + += form_for @order_cycle, :url => main_app.new_admin_order_cycle_path, method: :get do |f| + .row + .two.columns.alpha +   + .ten.columns + = select_tag :coordinator_id, options_for_select(order_cycle_coordinating_enterprise_options), { 'required' => true, class: 'select2 fullwidth'} + .two.columns.alpha + = f.submit "Continue >" + + .two.columns.omega +   diff --git a/spec/controllers/admin/order_cycles_controller_spec.rb b/spec/controllers/admin/order_cycles_controller_spec.rb new file mode 100644 index 0000000000..598059b5f4 --- /dev/null +++ b/spec/controllers/admin/order_cycles_controller_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +module Admin + describe OrderCyclesController do + include AuthenticationWorkflow + let!(:distributor_owner) { create_enterprise_user enterprise_limit: 2 } + + before do + controller.stub spree_current_user: distributor_owner + end + + describe "new" do + describe "when the user manages no distributor enterprises suitable for coordinator" do + let!(:distributor) { create(:distributor_enterprise, owner: distributor_owner, confirmed_at: nil) } + + it "redirects to order cycles index" do + spree_get :new + expect(response).to redirect_to admin_order_cycles_path + end + end + + describe "when the user manages a single distributor enterprise suitable for coordinator" do + let!(:distributor) { create(:distributor_enterprise, owner: distributor_owner) } + + it "renders the new template" do + spree_get :new + expect(response).to render_template :new + end + end + + describe "when a user manages multiple enterprises suitable for coordinator" do + let!(:distributor1) { create(:distributor_enterprise, owner: distributor_owner) } + let!(:distributor2) { create(:distributor_enterprise, owner: distributor_owner) } + let!(:distributor3) { create(:distributor_enterprise) } + + it "renders the set_coordinator template" do + spree_get :new + expect(response).to render_template :set_coordinator + end + + describe "and a coordinator_id is submitted as part of the request" do + describe "when the user manages the enterprise" do + it "renders the new template" do + spree_get :new, coordinator_id: distributor1.id + expect(response).to render_template :new + end + end + + describe "when the user does not manage the enterprise" do + it "renders the set_coordinator template and sets a flash error" do + spree_get :new, coordinator_id: distributor3.id + expect(response).to render_template :set_coordinator + expect(flash[:error]).to eq "You don't have permission to create an order cycle coordinated by that enterprise" + end + end + end + end + end + end +end diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 180485a734..c84a6d65c7 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -77,11 +77,14 @@ feature %q{ click_link 'Order Cycles' click_link 'New Order Cycle' + # Select a coordinator since there are two available + select2_select 'My coordinator', from: 'coordinator_id' + click_button "Continue >" + # And I fill in the basic fields fill_in 'order_cycle_name', with: 'Plums & Avos' fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00' fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00' - select 'My coordinator', from: 'order_cycle_coordinator_id' # And I add a coordinator fee click_button 'Add coordinator fee' @@ -502,7 +505,7 @@ feature %q{ # 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 show enterprises that I manage page.should have_selector 'td.suppliers', text: supplier_managed.name page.should have_selector 'td.distributors', text: distributor_managed.name @@ -516,6 +519,14 @@ feature %q{ click_link "Order Cycles" click_link 'New Order Cycle' + save_screenshot '/Users/rob/Desktop/ss.png' + + # Select a coordinator since there are two available + select2_select 'Managed distributor', from: 'coordinator_id' + click_button "Continue >" + + save_screenshot '/Users/rob/Desktop/ss1.png' + fill_in 'order_cycle_name', with: 'My order cycle' fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00' fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00' diff --git a/spec/helpers/order_cycles_helper_spec.rb b/spec/helpers/order_cycles_helper_spec.rb index 3706c72579..0e1ea42121 100644 --- a/spec/helpers/order_cycles_helper_spec.rb +++ b/spec/helpers/order_cycles_helper_spec.rb @@ -1,36 +1,36 @@ require 'spec_helper' describe OrderCyclesHelper do - describe "finding producer enterprises" do + describe "finding producer enterprise options" do before do - helper.stub_chain(:order_cycle_permitted_enterprises, :is_primary_producer, :by_name) { "enterprise list" } + helper.stub(:order_cycle_producer_enterprises) { "enterprise list" } end it "asks for a validation option list" do expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true}) - helper.order_cycle_producer_enterprises + helper.order_cycle_producer_enterprise_options end end - describe "finding coodinator enterprises" do + describe "finding coodinator enterprise options" do before do - helper.stub_chain(:order_cycle_permitted_enterprises, :is_distributor, :by_name) { "enterprise list" } + helper.stub(:order_cycle_coordinating_enterprises) { "enterprise list" } end it "asks for a validation option list" do expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true}) - helper.order_cycle_coordinating_enterprises + helper.order_cycle_coordinating_enterprise_options end end - describe "finding hub enterprises" do + describe "finding hub enterprise options" do before do - helper.stub_chain(:order_cycle_permitted_enterprises, :is_distributor, :by_name) { "enterprise list" } + helper.stub(:order_cycle_hub_enterprises) { "enterprise list" } end it "asks for a validation option list" do expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true, shipping_and_payment_methods: true}) - helper.order_cycle_hub_enterprises + helper.order_cycle_hub_enterprise_options end end From 5563d23beba6b2580f56d9bef676c89a77a79a64 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 25 Feb 2015 10:32:40 +1100 Subject: [PATCH 100/384] Rename set_coordinator method to require_coordinator --- app/controllers/admin/order_cycles_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index f978066bf4..75eae1f189 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -6,7 +6,7 @@ module Admin include OrderCyclesHelper before_filter :load_order_cycle_set, :only => :index - before_filter :set_coordinator, only: :new + before_filter :require_coordinator, only: :new def show respond_to do |format| @@ -87,7 +87,7 @@ module Admin @order_cycle_set = OrderCycleSet.new :collection => collection end - def set_coordinator + def require_coordinator if params[:coordinator_id] && @coordinator = order_cycle_coordinating_enterprises.find_by_id(params[:coordinator_id]) return end From 31054c7bf4bd17dbfc799b8a71ced56f6ca5e3fd Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 25 Feb 2015 11:07:04 +1100 Subject: [PATCH 101/384] Split order_cycle_simple_view logic into index and form scopings --- app/helpers/order_cycles_helper.rb | 8 ++++++-- app/views/admin/order_cycles/_row.html.haml | 2 +- app/views/admin/order_cycles/edit.html.haml | 4 ++-- app/views/admin/order_cycles/index.html.haml | 4 ++-- app/views/admin/order_cycles/new.html.haml | 4 ++-- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 66e6e16c20..711481f843 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -59,8 +59,12 @@ module OrderCyclesHelper OrderCycle.active.with_distributor(@distributor).present? end - def order_cycles_simple_view - @order_cycles_simple_view ||= !OpenFoodNetwork::Permissions.new(spree_current_user).can_manage_complex_order_cycles? + def order_cycles_simple_index + @order_cycles_simple_index ||= !OpenFoodNetwork::Permissions.new(spree_current_user).can_manage_complex_order_cycles? + end + + def order_cycles_simple_form + @order_cycles_simple_form ||= @order_cycle.coordinator.sells == 'own' end def order_cycles_enabled? diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 882484c091..de18679cf6 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -5,7 +5,7 @@ %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 - - unless order_cycles_simple_view + - unless order_cycles_simple_index %td.suppliers - order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |s| = s.name diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index d4a6751b48..2f17ecf85b 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -1,9 +1,9 @@ %h1 Edit Order Cycle -- ng_controller = order_cycles_simple_view ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' +- ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.order_cycles', 'ng-controller' => ng_controller, 'ng-submit' => 'submit($event)'} do |f| - - if order_cycles_simple_view + - if order_cycles_simple_form = render 'simple_form', f: f - else = render 'form', f: f diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index aa54f9bff9..995cfdd2c3 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -11,7 +11,7 @@ %col %col{'style' => 'width: 20%;'} %col{'style' => 'width: 20%;'} - - unless order_cycles_simple_view + - unless order_cycles_simple_index %col %col %col @@ -24,7 +24,7 @@ %th Name %th Open %th Close - - unless order_cycles_simple_view + - unless order_cycles_simple_index %th Suppliers %th Coordinator %th Distributors diff --git a/app/views/admin/order_cycles/new.html.haml b/app/views/admin/order_cycles/new.html.haml index 95974f9ec5..770eac0269 100644 --- a/app/views/admin/order_cycles/new.html.haml +++ b/app/views/admin/order_cycles/new.html.haml @@ -1,10 +1,10 @@ %h1 New Order Cycle -- ng_controller = order_cycles_simple_view ? 'AdminSimpleCreateOrderCycleCtrl' : 'AdminCreateOrderCycleCtrl' +- ng_controller = order_cycles_simple_form ? 'AdminSimpleCreateOrderCycleCtrl' : 'AdminCreateOrderCycleCtrl' = admin_inject_order_cycle_instance = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.order_cycles', 'ng-controller' => ng_controller, 'ng-submit' => 'submit($event)'} do |f| - - if order_cycles_simple_view + - if order_cycles_simple_form = render 'simple_form', f: f - else = render 'form', f: f From 8d6c8791bb00b1ea457f7ce476a78073aade0477 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 25 Feb 2015 11:52:09 +1100 Subject: [PATCH 102/384] Only hubs (sells any) are available for selection as outgoing enterprises --- app/helpers/order_cycles_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 711481f843..6e401af0a6 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -24,7 +24,7 @@ module OrderCyclesHelper end def order_cycle_hub_enterprises - order_cycle_permitted_enterprises.is_distributor.by_name + order_cycle_permitted_enterprises.is_hub.by_name end def order_cycle_hub_enterprise_options From c91699a11e9f5c4c6f98fba4b23d6ef4b7369f01 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Feb 2015 17:22:42 +1100 Subject: [PATCH 103/384] Using @order_cycle.coordinator instead of @coordinator --- app/controllers/admin/order_cycles_controller.rb | 4 ++-- app/helpers/admin/injection_helper.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 75eae1f189..04a6d234c6 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -88,7 +88,7 @@ module Admin end def require_coordinator - if params[:coordinator_id] && @coordinator = order_cycle_coordinating_enterprises.find_by_id(params[:coordinator_id]) + if params[:coordinator_id] && @order_cycle.coordinator = order_cycle_coordinating_enterprises.find_by_id(params[:coordinator_id]) return end @@ -98,7 +98,7 @@ module Admin flash[:error] = "None of your enterprises have permission to coordinate an order cycle" redirect_to main_app.admin_order_cycles_path when 1 - @coordinator = available_coordinators.first + @order_cycle.coordinator = available_coordinators.first else flash[:error] = "You don't have permission to create an order cycle coordinated by that enterprise" if params[:coordinator_id] render :set_coordinator diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 755ba1f5a3..14c4f9c20d 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -63,7 +63,7 @@ module Admin end def admin_inject_order_cycle_instance - render partial: "admin/json/injection_ams", locals: {ngModule: 'admin.order_cycles', name: 'ocInstance', json: "{coordinator_id: '#{@coordinator.id}'}"} + render partial: "admin/json/injection_ams", locals: {ngModule: 'admin.order_cycles', name: 'ocInstance', json: "{coordinator_id: '#{@order_cycle.coordinator.id}'}"} end def admin_inject_spree_api_key From e8818c519128f7da15a690645084ab07a31dab47 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 1 Mar 2015 12:22:06 +1100 Subject: [PATCH 104/384] Adding a ship address to order in spec --- spec/mailers/order_mailer_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/mailers/order_mailer_spec.rb b/spec/mailers/order_mailer_spec.rb index 3b8de23e24..a472e95979 100644 --- a/spec/mailers/order_mailer_spec.rb +++ b/spec/mailers/order_mailer_spec.rb @@ -16,7 +16,8 @@ describe Spree::OrderMailer do product = create(:product) product_distribution = create(:product_distribution, :product => product, :distributor => @distributor) @shipping_instructions = "pick up on thursday please!" - @order1 = create(:order, :distributor => @distributor, :bill_address => @bill_address, :special_instructions => @shipping_instructions) + ship_address = create(:address, :address1 => "distributor address", :city => 'The Shire', :zipcode => "1234") + @order1 = create(:order, :distributor => @distributor, :bill_address => @bill_address, ship_address: ship_address, :special_instructions => @shipping_instructions) ActionMailer::Base.deliveries = [] end From 85b8adb1aa0e6af95e99cfb3370c8745c7589697 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 1 Mar 2015 15:33:16 +1100 Subject: [PATCH 105/384] call to order_cycle_hub_enterprises has no arguments --- app/controllers/admin/variant_overrides_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index 234c2779c4..6724896eb9 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -8,7 +8,7 @@ module Admin before_filter :load_spree_api_key, only: :index def index - @hubs = order_cycle_hub_enterprises(without_validation: true) + @hubs = order_cycle_hub_enterprises # Used in JS to look up the name of the producer of each product @producers = OpenFoodNetwork::Permissions.new(spree_current_user). From 87b1ab9a1acfaa2624868c475c202068bc3acd89 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 24 Feb 2015 13:43:00 +1000 Subject: [PATCH 106/384] LD: Adding additional functionality for order cycle management reports - adding hub code model, adding temp_controlled bool, adding delivery report --- .../admin/reports_controller_decorator.rb | 5 +- app/models/customer.rb | 2 + db/migrate/20150122145607_create_customers.rb | 13 ++++ ...controlled_to_spree_shipping_categories.rb | 5 ++ db/schema.rb | 18 ++++- .../order_cycle_management_report.rb | 78 +++++++++++++++---- spec/features/admin/reports_spec.rb | 15 +++- 7 files changed, 113 insertions(+), 23 deletions(-) create mode 100644 app/models/customer.rb create mode 100644 db/migrate/20150122145607_create_customers.rb create mode 100644 db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 655d1778dc..50954950e0 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -27,7 +27,8 @@ Spree::Admin::ReportsController.class_eval do ["Addresses", :addresses] ], order_cycle_management: [ - ["Payment Methods Report", :payment_methods_report] + ["Payment Methods Report", :payment_methods], + ["Delivery Report", :delivery] ] } @@ -58,7 +59,6 @@ Spree::Admin::ReportsController.class_eval do @report_types = REPORT_TYPES[:customers] @report_type = params[:report_type] @report = OpenFoodNetwork::CustomersReport.new spree_current_user, params - render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv") end @@ -68,7 +68,6 @@ Spree::Admin::ReportsController.class_eval do @report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user, params @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) - @orders = @search.result render_report(@report.header, @report.table, params[:csv], "order_cycle_management_#{timestamp}.csv") diff --git a/app/models/customer.rb b/app/models/customer.rb new file mode 100644 index 0000000000..047dcb0c04 --- /dev/null +++ b/app/models/customer.rb @@ -0,0 +1,2 @@ +class Customer < ActiveRecord::Base +end diff --git a/db/migrate/20150122145607_create_customers.rb b/db/migrate/20150122145607_create_customers.rb new file mode 100644 index 0000000000..7993f9a4b6 --- /dev/null +++ b/db/migrate/20150122145607_create_customers.rb @@ -0,0 +1,13 @@ +class CreateCustomers < ActiveRecord::Migration + def change + create_table :customers do |t| + t.string :email + t.references :enterprise + t.string :code + + t.timestamps + end + add_index :customers, [:enterprise_id, :code], unique: true + add_index :customers, :email + end +end diff --git a/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb new file mode 100644 index 0000000000..d939d5fdf3 --- /dev/null +++ b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb @@ -0,0 +1,5 @@ +class AddTemperatureControlledToSpreeShippingCategories < ActiveRecord::Migration + def change + add_column :spree_shipping_categories, :temperature_controlled, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index a8ac91e6d8..b4c7f4e436 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -155,6 +155,17 @@ ActiveRecord::Schema.define(:version => 20150220035501) do add_index "coordinator_fees", ["enterprise_fee_id"], :name => "index_coordinator_fees_on_enterprise_fee_id" add_index "coordinator_fees", ["order_cycle_id"], :name => "index_coordinator_fees_on_order_cycle_id" + create_table "customers", :force => true do |t| + t.string "email" + t.integer "enterprise_id" + t.string "code" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "customers", ["email"], :name => "index_customers_on_email" + add_index "customers", ["enterprise_id", "code"], :name => "index_customers_on_enterprise_id_and_code", :unique => true + create_table "distributors_payment_methods", :id => false, :force => true do |t| t.integer "distributor_id" t.integer "payment_method_id" @@ -585,9 +596,9 @@ ActiveRecord::Schema.define(:version => 20150220035501) do t.string "email" t.text "special_instructions" t.integer "distributor_id" - t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" + t.integer "order_cycle_id" t.integer "cart_id" end @@ -848,8 +859,9 @@ ActiveRecord::Schema.define(:version => 20150220035501) do create_table "spree_shipping_categories", :force => true do |t| t.string "name" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "temperature_controlled" end create_table "spree_shipping_methods", :force => true do |t| diff --git a/lib/open_food_network/order_cycle_management_report.rb b/lib/open_food_network/order_cycle_management_report.rb index 06a90bfe25..ac98b062c4 100644 --- a/lib/open_food_network/order_cycle_management_report.rb +++ b/lib/open_food_network/order_cycle_management_report.rb @@ -1,3 +1,5 @@ +require 'open_food_network/user_balance_calculator' + module OpenFoodNetwork class OrderCycleManagementReport attr_reader :params @@ -7,23 +9,19 @@ module OpenFoodNetwork end def header - ["First Name", "Last Name", "Email", "Phone", "Hub", "Shipping Method", "Payment Method", "Amount"] + if is_payment_methods? + ["First Name", "Last Name", "Hub", "Hub Code", "Email", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance"] + else + ["First Name", "Last Name", "Hub", "Hub Code", "Delivery Address", "Delivery Postcode", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance", "Temp Controlled Items?", "Special Instructions"] + end end def table - orders.map do |order| - ba = order.billing_address - da = order.distributor.andand.address - [ba.firstname, - ba.lastname, - order.email, - ba.phone, - order.distributor.andand.name, - order.shipping_method.andand.name, - order.payments.first.andand.payment_method.andand.name, - order.payments.first.amount - ] - end + if is_payment_methods? + orders.map { |o| payment_method_row o } + else + orders.map { |o| delivery_row o } + end end def orders @@ -34,6 +32,44 @@ module OpenFoodNetwork filter_to_order_cycle filter_to_payment_method filter_to_shipping_method orders end + + private + + def payment_method_row (order) + ba = order.billing_address + da = order.distributor.andand.address + [ba.firstname, + ba.lastname, + order.distributor.andand.name, + customer_code(order.email), + order.email, + ba.phone, + order.shipping_method.andand.name, + order.payments.first.andand.payment_method.andand.name, + order.payments.first.amount, + OpenFoodNetwork::UserBalanceCalculator.new(order.user, order.distributor).balance + ] + end + + def delivery_row (order) + ba = order.billing_address + da = order.distributor.andand.address + [ba.firstname, + ba.lastname, + order.distributor.andand.name, + customer_code(order.email), + "#{ba.address1} #{ba.address2} #{ba.city}", + ba.zipcode, + ba.phone, + order.shipping_method.andand.name, + order.payments.first.andand.payment_method.andand.name, + order.payments.first.amount, + OpenFoodNetwork::UserBalanceCalculator.new(order.user, order.distributor).balance, + has_temperature_controlled_items?(order), + order.special_instructions + ] + end + def filter_to_payment_method(orders) if params[:payment_method_name].present? orders.with_payment_method_name(params[:payment_method_name]) @@ -57,5 +93,19 @@ module OpenFoodNetwork orders end end + + def has_temperature_controlled_items?(order) + order.line_items.any? { |line_item| line_item.product.shipping_category.nil? ? + false : line_item.product.shipping_category.temperature_controlled? } + end + + def is_payment_methods? + params[:report_type] == "payment_methods" + end + + def customer_code (email) + customer = Customer.where(email: email).first + customer.nil? ? "" : customer.code + end end end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 0524a6769e..0956f268d3 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -65,12 +65,21 @@ feature %q{ click_link "Reports" end - scenario "order payment method report" do - click_link "Order Cycle Management" + scenario "payment method report" do + click_link "Payment Methods Report" rows = find("table#listing_order_payment_methods").all("thead tr") table = rows.map { |r| r.all("th").map { |c| c.text.strip } } table.sort.should == [ - ["First Name", "Last Name", "Email", "Phone", "Hub", "Shipping Method", "Payment Method", "Amount"] + ["First Name", "Last Name", "Hub", "Hub Code", "Email", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance"] + ].sort + end + + scenario "delivery report" do + click_link "Delivery Report" + rows = find("table#listing_order_payment_methods").all("thead tr") + table = rows.map { |r| r.all("th").map { |c| c.text.strip } } + table.sort.should == [ + ["First Name", "Last Name", "Hub", "Hub Code", "Delivery Address", "Delivery Postcode", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance", "Temp Controlled Items?", "Special Instructions"] ].sort end end From 39c5f8febec705d0b162f9f02c0ac45498e6c147 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Mon, 2 Mar 2015 12:32:28 +1100 Subject: [PATCH 107/384] Updating Customer model with validations --- app/models/customer.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/models/customer.rb b/app/models/customer.rb index 047dcb0c04..3dbdd59a67 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,2 +1,8 @@ class Customer < ActiveRecord::Base + + belongs_to :enterprise + + validates :code, presence: true, uniqueness: {scope: :enterprise_id} + validates :email, presence: true + validates :enterprise_id, presence: true end From f2a35e219e9d1a4b41f5d45446cc50713f9ef57c Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Mon, 2 Mar 2015 12:59:07 +1100 Subject: [PATCH 108/384] Updating migration on customers table with extra validation --- db/migrate/20150122145607_create_customers.rb | 11 ++++++++--- db/schema.rb | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/db/migrate/20150122145607_create_customers.rb b/db/migrate/20150122145607_create_customers.rb index 7993f9a4b6..12e5a721e8 100644 --- a/db/migrate/20150122145607_create_customers.rb +++ b/db/migrate/20150122145607_create_customers.rb @@ -1,13 +1,18 @@ class CreateCustomers < ActiveRecord::Migration def change create_table :customers do |t| - t.string :email - t.references :enterprise - t.string :code + t.string :email, null: false + t.references :enterprise, null: false + t.string :code, null: false + t.references :user t.timestamps end add_index :customers, [:enterprise_id, :code], unique: true add_index :customers, :email + add_index :customers, :user_id + + add_foreign_key :customers, :enterprises, column: :enterprise_id + add_foreign_key :customers, :spree_users, column: :user_id end end diff --git a/db/schema.rb b/db/schema.rb index b4c7f4e436..2d4abcce6c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -156,15 +156,17 @@ ActiveRecord::Schema.define(:version => 20150220035501) do add_index "coordinator_fees", ["order_cycle_id"], :name => "index_coordinator_fees_on_order_cycle_id" create_table "customers", :force => true do |t| - t.string "email" - t.integer "enterprise_id" - t.string "code" + t.string "email", :null => false + t.integer "enterprise_id", :null => false + t.string "code", :null => false + t.integer "user_id" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end add_index "customers", ["email"], :name => "index_customers_on_email" add_index "customers", ["enterprise_id", "code"], :name => "index_customers_on_enterprise_id_and_code", :unique => true + add_index "customers", ["user_id"], :name => "index_customers_on_user_id" create_table "distributors_payment_methods", :id => false, :force => true do |t| t.integer "distributor_id" @@ -1093,6 +1095,9 @@ ActiveRecord::Schema.define(:version => 20150220035501) do 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 "customers", "enterprises", name: "customers_enterprise_id_fk" + add_foreign_key "customers", "spree_users", name: "customers_user_id_fk", column: "user_id" + 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" From 3279003fd318baf27bfde0c76e96ec906f1b1b19 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 3 Mar 2015 09:35:26 +1100 Subject: [PATCH 109/384] Updating logic on temperature_controlled lookup --- lib/open_food_network/order_cycle_management_report.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/open_food_network/order_cycle_management_report.rb b/lib/open_food_network/order_cycle_management_report.rb index ac98b062c4..09b9449a82 100644 --- a/lib/open_food_network/order_cycle_management_report.rb +++ b/lib/open_food_network/order_cycle_management_report.rb @@ -95,8 +95,7 @@ module OpenFoodNetwork end def has_temperature_controlled_items?(order) - order.line_items.any? { |line_item| line_item.product.shipping_category.nil? ? - false : line_item.product.shipping_category.temperature_controlled? } + order.line_items.any? { |line_item| line_item.product.shipping_category.andand.temperature_controlled } end def is_payment_methods? From 2a8ba0bf425f369dd682448d10537f8dbbd2a238 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 3 Mar 2015 16:12:07 +1100 Subject: [PATCH 110/384] Fixing merge conflicts after rebase --- app/models/customer.rb | 1 - db/schema.rb | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index 3dbdd59a67..f09ffe2e7d 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,5 +1,4 @@ class Customer < ActiveRecord::Base - belongs_to :enterprise validates :code, presence: true, uniqueness: {scope: :enterprise_id} diff --git a/db/schema.rb b/db/schema.rb index 2d4abcce6c..0fc5e6340a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -156,17 +156,26 @@ ActiveRecord::Schema.define(:version => 20150220035501) do add_index "coordinator_fees", ["order_cycle_id"], :name => "index_coordinator_fees_on_order_cycle_id" create_table "customers", :force => true do |t| +<<<<<<< HEAD t.string "email", :null => false t.integer "enterprise_id", :null => false t.string "code", :null => false t.integer "user_id" +======= + t.string "email" + t.integer "enterprise_id" + t.string "code" +>>>>>>> a3df4bf0262154d8dfa3667f6e20d09fc8dca77c t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end add_index "customers", ["email"], :name => "index_customers_on_email" add_index "customers", ["enterprise_id", "code"], :name => "index_customers_on_enterprise_id_and_code", :unique => true +<<<<<<< HEAD add_index "customers", ["user_id"], :name => "index_customers_on_user_id" +======= +>>>>>>> a3df4bf0262154d8dfa3667f6e20d09fc8dca77c create_table "distributors_payment_methods", :id => false, :force => true do |t| t.integer "distributor_id" From 3ea7ff766a7336a9f57ca82f58805798865e6e41 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 3 Mar 2015 16:29:17 +1100 Subject: [PATCH 112/384] Removing additional merge conflicts --- db/schema.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 0fc5e6340a..2d4abcce6c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -156,26 +156,17 @@ ActiveRecord::Schema.define(:version => 20150220035501) do add_index "coordinator_fees", ["order_cycle_id"], :name => "index_coordinator_fees_on_order_cycle_id" create_table "customers", :force => true do |t| -<<<<<<< HEAD t.string "email", :null => false t.integer "enterprise_id", :null => false t.string "code", :null => false t.integer "user_id" -======= - t.string "email" - t.integer "enterprise_id" - t.string "code" ->>>>>>> a3df4bf0262154d8dfa3667f6e20d09fc8dca77c t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end add_index "customers", ["email"], :name => "index_customers_on_email" add_index "customers", ["enterprise_id", "code"], :name => "index_customers_on_enterprise_id_and_code", :unique => true -<<<<<<< HEAD add_index "customers", ["user_id"], :name => "index_customers_on_user_id" -======= ->>>>>>> a3df4bf0262154d8dfa3667f6e20d09fc8dca77c create_table "distributors_payment_methods", :id => false, :force => true do |t| t.integer "distributor_id" From 72a568ffad44affa76cdff09759f25e796bdc524 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 3 Mar 2015 16:33:49 +1100 Subject: [PATCH 113/384] Adding not null to temp_controlled field of shipping_categories --- ...6_add_temperature_controlled_to_spree_shipping_categories.rb | 2 +- db/schema.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb index d939d5fdf3..89035fc16f 100644 --- a/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb +++ b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb @@ -1,5 +1,5 @@ class AddTemperatureControlledToSpreeShippingCategories < ActiveRecord::Migration def change - add_column :spree_shipping_categories, :temperature_controlled, :boolean + add_column :spree_shipping_categories, :temperature_controlled, :boolean, null: false, default: false end end diff --git a/db/schema.rb b/db/schema.rb index 2d4abcce6c..9bbc6da0ca 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -863,7 +863,7 @@ ActiveRecord::Schema.define(:version => 20150220035501) do t.string "name" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false - t.boolean "temperature_controlled" + t.boolean "temperature_controlled", :null => false end create_table "spree_shipping_methods", :force => true do |t| From b26c46d7db3587e057b899280966b32f4411b0ec Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 3 Mar 2015 16:39:55 +1100 Subject: [PATCH 114/384] Removing default false from temp_controlled - shipping categories --- ...6_add_temperature_controlled_to_spree_shipping_categories.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb index 89035fc16f..9ddf19c81e 100644 --- a/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb +++ b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb @@ -1,5 +1,5 @@ class AddTemperatureControlledToSpreeShippingCategories < ActiveRecord::Migration def change - add_column :spree_shipping_categories, :temperature_controlled, :boolean, null: false, default: false + add_column :spree_shipping_categories, :temperature_controlled, :boolean, null: false end end From 7ba366e2a6a7aa9b5bb256bd387f66404f081d85 Mon Sep 17 00:00:00 2001 From: Liv Galendez Date: Tue, 3 Mar 2015 20:46:25 +1100 Subject: [PATCH 115/384] Added select for coordinator since there are 2 now --- spec/features/admin/order_cycles_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index c84a6d65c7..b1ad3a8843 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -85,6 +85,7 @@ feature %q{ fill_in 'order_cycle_name', with: 'Plums & Avos' fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00' fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00' + select 'My coordinator', from: 'order_cycle_coordinator_id' # And I add a coordinator fee click_button 'Add coordinator fee' From fc63d8719c2ad8960e597dc4d3cba5b11f893a1c Mon Sep 17 00:00:00 2001 From: Liv Galendez Date: Tue, 3 Mar 2015 20:53:39 +1100 Subject: [PATCH 116/384] Used order_cycle_permitted_enterprises helper in order cycles row --- app/views/admin/order_cycles/_row.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index de18679cf6..9d05ac176d 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -7,12 +7,12 @@ - unless order_cycles_simple_index %td.suppliers - - order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |s| + - order_cycle.suppliers.merge(order_cycle_permitted_enterprises).each do |s| = s.name %br/ %td= order_cycle.coordinator.name %td.distributors - - order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |d| + - order_cycle.distributors.merge(order_cycle_permitted_enterprises).each do |d| = d.name %br/ From c940a34ec9a182aa1aae2c30814db9826acc6cef Mon Sep 17 00:00:00 2001 From: Liv Galendez Date: Tue, 3 Mar 2015 21:03:01 +1100 Subject: [PATCH 117/384] Created order_cycle_permitted_in helper --- app/helpers/order_cycles_helper.rb | 4 ++++ app/views/admin/order_cycles/_row.html.haml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 6e401af0a6..b870f9c985 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -3,6 +3,10 @@ module OrderCyclesHelper @current_order_cycle ||= current_order(false).andand.order_cycle end + def order_cycle_permitted_in(enterprises) + enterprises.merge(order_cycle_permitted_enterprises) + end + def order_cycle_permitted_enterprises OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises end diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 9d05ac176d..91191a2a8a 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -7,12 +7,12 @@ - unless order_cycles_simple_index %td.suppliers - - order_cycle.suppliers.merge(order_cycle_permitted_enterprises).each do |s| + - order_cycle_permitted_in(order_cycle.suppliers).each do |s| = s.name %br/ %td= order_cycle.coordinator.name %td.distributors - - order_cycle.distributors.merge(order_cycle_permitted_enterprises).each do |d| + - order_cycle_permitted_in(order_cycle.distributors).each do |d| = d.name %br/ From 397697d17c4bbbdd23053e304f1eaea21eee0032 Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 11:13:58 +1100 Subject: [PATCH 118/384] Tweak layout. Remove unnecessary row which was messing up the responsive layout for left column on checkout. --- app/views/checkout/edit.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index 2744627d03..2f30c93ba5 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -16,7 +16,7 @@ .small-12.medium-8.large-9.columns - unless spree_current_user = render partial: "checkout/authentication" - .row{"ng-show" => "enabled", "ng-controller" => "AccordionCtrl"} + %div{"ng-show" => "enabled", "ng-controller" => "AccordionCtrl"} = render partial: "checkout/form" .small-12.medium-4.large-3.columns = render partial: "checkout/summary" From f64684dc48984a688668f67fbf5da3db5d25bfea Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 11:14:29 +1100 Subject: [PATCH 119/384] Tweak responsive heading columns now that container is a bit smaller --- app/views/checkout/_accordion_heading.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/checkout/_accordion_heading.html.haml b/app/views/checkout/_accordion_heading.html.haml index 76724cd29e..c7872ce109 100644 --- a/app/views/checkout/_accordion_heading.html.haml +++ b/app/views/checkout/_accordion_heading.html.haml @@ -1,10 +1,10 @@ %accordion-heading .row - .small-8.medium-10.columns + .small-8.medium-9.columns %em %small {{ summary() | printArray }} - .small-4.medium-2.columns.text-right + .small-4.medium-3.columns.text-right %span.accordion-up %em %small Hide From cfa281478a14c31b1744f6da3c3493e3a2c52b0a Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Wed, 4 Mar 2015 12:02:07 +1100 Subject: [PATCH 120/384] Adding ability to edit ShippingCategory.temperature_controlled from admin interface --- app/models/spree/shipping_category_decorator.rb | 3 +++ .../add_temperature_controlled_form_element.html.haml.deface | 5 +++++ .../index/add_temp_controlled_td.html.haml.deface | 5 +++++ .../index/add_temp_controlled_th.html.haml.deface | 4 ++++ 4 files changed, 17 insertions(+) create mode 100644 app/models/spree/shipping_category_decorator.rb create mode 100644 app/overrides/spree/admin/shipping_categories/_form/add_temperature_controlled_form_element.html.haml.deface create mode 100644 app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface create mode 100644 app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_th.html.haml.deface diff --git a/app/models/spree/shipping_category_decorator.rb b/app/models/spree/shipping_category_decorator.rb new file mode 100644 index 0000000000..b78ba3337c --- /dev/null +++ b/app/models/spree/shipping_category_decorator.rb @@ -0,0 +1,3 @@ +Spree::ShippingCategory.class_eval do + attr_accessible :temperature_controlled +end diff --git a/app/overrides/spree/admin/shipping_categories/_form/add_temperature_controlled_form_element.html.haml.deface b/app/overrides/spree/admin/shipping_categories/_form/add_temperature_controlled_form_element.html.haml.deface new file mode 100644 index 0000000000..4e9652bbd3 --- /dev/null +++ b/app/overrides/spree/admin/shipping_categories/_form/add_temperature_controlled_form_element.html.haml.deface @@ -0,0 +1,5 @@ +/ insert_bottom "div[data-hook='admin_shipping_category_form_fields']" + +%div.field.align-center{data-hook => "name"} + = f.label :temperature_controlled, t(:temperature_controlled) + = f.check_box :temperature_controlled diff --git a/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface b/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface new file mode 100644 index 0000000000..ce3d7304db --- /dev/null +++ b/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface @@ -0,0 +1,5 @@ +/ insert_after "[data-hook='category_row'] td:first-child" + +%td.align-center + = shipping_category.temperature_controlled + %br/ diff --git a/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_th.html.haml.deface b/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_th.html.haml.deface new file mode 100644 index 0000000000..561e59bf91 --- /dev/null +++ b/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_th.html.haml.deface @@ -0,0 +1,4 @@ +/ insert_after "[data-hook='categories_header'] th:first-child" + +%th + Temperature Controlled From b097a62c7a442b1236e6c3e5eb9d34b753a966ad Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 12:37:57 +1100 Subject: [PATCH 121/384] WIP adding variant name logic to the email templates --- .../order_mailer/confirm_email_for_customer.html.haml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 497649f844..8b3e93c92b 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -36,8 +36,14 @@ - @order.line_items.each do |item| %tr %td - %strong= "#{raw(item.variant.product.name)}" - = "#{raw(item.variant.options_text)}" + - if item.variant.product.name == item.variant.name_to_display + %strong= "#{raw(item.variant.product.name)}" + - else + %strong + %span= "#{raw(item.variant.product.name)}" + %span.light= "- " + "#{raw(item.variant.name_to_display)}" + - if item.variant.options_text + = "(" + "#{raw(item.variant.options_text)}" + ")" %br %small %em= raw(item.variant.product.supplier.name) From 6e800341c359356801d529ff9cfd5a2cf5fe6c0f Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Wed, 4 Mar 2015 16:00:46 +1100 Subject: [PATCH 122/384] Fixes issue #362 Change admin account link to point to account page instead of edit user. --- app/views/spree/layouts/admin/_login_nav.html.haml | 2 +- spec/features/admin/authentication_spec.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/views/spree/layouts/admin/_login_nav.html.haml b/app/views/spree/layouts/admin/_login_nav.html.haml index 4ecb72d148..088ac02377 100644 --- a/app/views/spree/layouts/admin/_login_nav.html.haml +++ b/app/views/spree/layouts/admin/_login_nav.html.haml @@ -5,7 +5,7 @@ \: #{spree_current_user.email} %li{"data-hook" => "user-account-link"} %i.icon-user - = link_to t(:account), spree.edit_user_path(spree_current_user) + = link_to t(:account), account_path %li{"data-hook" => "user-logout-link"} %i.icon-signout = link_to t(:logout), spree.logout_path diff --git a/spec/features/admin/authentication_spec.rb b/spec/features/admin/authentication_spec.rb index beb8c31c2b..12e9795ad2 100644 --- a/spec/features/admin/authentication_spec.rb +++ b/spec/features/admin/authentication_spec.rb @@ -2,6 +2,9 @@ require 'spec_helper' feature "Authentication", js: true do include UIComponentHelper + include AuthenticationWorkflow + include WebHelper + let(:user) { create(:user, password: "password", password_confirmation: "password") } scenario "logging into admin redirects home, then back to admin" do @@ -16,4 +19,10 @@ feature "Authentication", js: true do page.should have_content "Dashboard" current_path.should == spree.admin_path end + + scenario "viewing my account" do + login_to_admin_section + click_link "Account" + current_path.should == spree.account_path + end end From 163035dcad7fe88869a1afc862707ea402c3a407 Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 17:18:37 +1100 Subject: [PATCH 123/384] Tweaking email template to be more like order confirmation --- .../confirm_email_for_customer.html.haml | 31 +++++++-------- .../confirm_email_for_shop.html.haml | 38 ++++++++++--------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 8b3e93c92b..5fe2764607 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -41,7 +41,7 @@ - else %strong %span= "#{raw(item.variant.product.name)}" - %span.light= "- " + "#{raw(item.variant.name_to_display)}" + %span= "- " + "#{raw(item.variant.name_to_display)}" - if item.variant.options_text = "(" + "#{raw(item.variant.options_text)}" + ")" %br @@ -98,24 +98,21 @@ %h4 Delivery on: %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - %br   - - %h4 Delivery address: - %p - #{@order.ship_address.full_name} - %br - #{@order.ship_address.full_address} - %br - #{@order.ship_address.phone} - - if @order.shipping_method.andand.description - %p #{@order.shipping_method.description.html_safe} + %p + %em #{@order.shipping_method.description.html_safe} + %br   + + - if @order.ship_address + %h4 Delivery address: + %p + #{@order.ship_address.full_name} + %br + #{@order.ship_address.full_address} + %br + #{@order.ship_address.phone} %br   - / - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - / %h4 Other delivery info: - / %p - / %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - else / Collection details @@ -155,7 +152,7 @@ %br %p %small - %strong Customer notes / custom delivery instructions: + %strong Your notes / custom delivery instructions: %br #{@order.special_instructions} diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index c2dc363876..68ccfe099f 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -36,8 +36,14 @@ - @order.line_items.each do |item| %tr %td - %strong= "#{raw(item.variant.product.name)}" - = "#{raw(item.variant.options_text)}" + - if item.variant.product.name == item.variant.name_to_display + %strong= "#{raw(item.variant.product.name)}" + - else + %strong + %span= "#{raw(item.variant.product.name)}" + %span= "- " + "#{raw(item.variant.name_to_display)}" + - if item.variant.options_text + = "(" + "#{raw(item.variant.options_text)}" + ")" %br %small %em= raw(item.variant.product.supplier.name) @@ -92,25 +98,21 @@ %h4 Delivery on: %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - %br   - - %h4 Delivery address: - %p - #{@order.ship_address.full_name} - %br - #{@order.ship_address.full_address} - %br - #{@order.ship_address.phone} - - if @order.shipping_method.andand.description - %p #{@order.shipping_method.description.html_safe} + %p + %em #{@order.shipping_method.description.html_safe} + %br   + + - if @order.ship_address + %h4 Delivery address: + %p + #{@order.ship_address.full_name} + %br + #{@order.ship_address.full_address} + %br + #{@order.ship_address.phone} %br   - / - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) - / %h4 Other delivery info: - / %p - / %strong #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - else / Collection details %p.callout From 76f234ee4d6a4cc8a85d832c8a25404457c009c8 Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 17:18:55 +1100 Subject: [PATCH 124/384] Order confirmation top page working on header label --- app/views/spree/orders/show.html.haml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/app/views/spree/orders/show.html.haml b/app/views/spree/orders/show.html.haml index 1f020f8c1f..f47a3c2d36 100644 --- a/app/views/spree/orders/show.html.haml +++ b/app/views/spree/orders/show.html.haml @@ -11,17 +11,20 @@ = render partial: "shopping_shared/details" - .row - %fieldset#order_summary{"data-hook" => ""} - %legend{align: "center"}= t(:order) + " #" + @order.number + %fieldset#order_summary{"data-hook" => ""} + .row + .columns.large-12.text-center + %h2 + Order confirmation + = " #" + @order.number + + #order{"data-hook" => ""} + - if params.has_key? :checkout_complete + %h1= t(:thank_you_for_your_order) - #order{"data-hook" => ""} - - if params.has_key? :checkout_complete - %h1= t(:thank_you_for_your_order) + = render :partial => 'spree/shared/order_details', :locals => { :order => @order } - = render :partial => 'spree/shared/order_details', :locals => { :order => @order } - - = link_to t(:back_to_store), main_app.shop_path, :class => "button" - - unless params.has_key? :checkout_complete - - if try_spree_current_user && respond_to?(:spree_account_path) - = link_to t(:my_account), spree_account_path, :class => "button" + = link_to t(:back_to_store), main_app.shop_path, :class => "button" + - unless params.has_key? :checkout_complete + - if try_spree_current_user && respond_to?(:spree_account_path) + = link_to t(:my_account), spree_account_path, :class => "button" From 47b08269d685ef23970e17d9ec37dcfbf21ca918 Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 17:19:24 +1100 Subject: [PATCH 125/384] Order confirmation page changes for responsive layout, adding in more content from the checkout page to match the emails. --- .../spree/shared/_order_details.html.haml | 102 ++++++++++++------ 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index a3308ced34..8885ccd111 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -1,32 +1,67 @@ .row - - if order.has_step?("address") - .columns.large-3 - %h6 - = t(:shipping_address) + .columns.large-6 + .order-summary.text-small + .right + - if @order.paid? + PAID + - else + NOT PAID + %span + Total order + %strong + = order.display_total.to_html + / = link_to "(#{t(:edit)})", checkout_state_path(:payment) unless @order.completed? + %p.text-big + Paying via: + %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe + %br + %span.text-small + %em + = order.payments.first.andand.payment_method.andand.description.andand.html_safe + .order-summary.text-small + %strong + Billing address = link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? - .address - = order.ship_address - .columns.large-3 - %h6 - = t(:billing_address) - = link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? - .address - = order.bill_address + %p.text-small + = order.bill_address.firstname + " " + order.bill_address.lastname + %br + = order.bill_address.full_address + + .columns.large-6 + .order-summary.text-small + %strong= order.shipping_method.name + - if @order.has_step?("delivery") - .columns.large-2 - %h6 - = t(:shipping_method) - .delivery - = order.shipping_method.name - .columns.large-4 - %h6 - = t(:payment_information) - = link_to "(#{t(:edit)})", checkout_state_path(:payment) unless @order.completed? - .payment-info - = render order.payments.valid - -%hr/ + %p.text-big + Delivery on + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %br + %span.text-small + %em= order.shipping_method.description.html_safe + .order-summary.text-small + %strong Delivery address + %p.text-small + = order.ship_address.firstname + " " + order.ship_address.lastname + %br + = order.ship_address.full_address + + - else + .order-summary.text-small + %strong + Shipping address + = link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? + %p.text-small + = order.ship_address.firstname + " " + order.ship_address.lastname + %br + = order.ship_address.full_address + %p.light + %small + %strong Your notes / custom instructions: + %br + = order.special_instructions + +%br %table#line-items{"data-hook" => "order_details"} %col{halign: "center", valign: "middle", width: "15%"}/ %col{valign: "middle", width: "70%"}/ @@ -48,10 +83,14 @@ = link_to small_image(item.variant.product), item.variant.product - else = link_to image_tag(item.variant.images.first.attachment.url(:small)), item.variant.product - %td{"data-hook" => "order_item_description"} - %h4= item.variant.product.name - = truncated_product_description(item.variant.product) - = "(" + item.variant.options_text + ")" unless item.variant.option_values.empty? + %td(data-hook = "order_item_description") + - if item.variant.product.name == item.variant.name_to_display + %h5= item.variant.product.name + - else + %h5 + %span= item.variant.product.name + %span= "- " + item.variant.name_to_display + %p= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? %td.price{"data-hook" => "order_item_price"} %span= item.single_display_amount_with_adjustments.to_html %td{"data-hook" => "order_item_qty"}= item.quantity @@ -81,8 +120,7 @@ %tfoot#subtotal{"data-hook" => "order_details_subtotal"} %tr#subtotal-row.total %td{colspan: "4"} - %b - Produce: + Produce: %td.total %span= display_checkout_subtotal(@order) @@ -90,6 +128,6 @@ - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| %tr.total %td{:colspan => "4"} - %strong= adjustment.label + ":" + = adjustment.label + ":" %td.total %span= adjustment.display_amount.to_html From 4867f45ee1e52fab600e811d9d14dbd7798edc24 Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 17:19:37 +1100 Subject: [PATCH 126/384] Add an extra style --- app/assets/stylesheets/darkswarm/checkout.css.sass | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/darkswarm/checkout.css.sass b/app/assets/stylesheets/darkswarm/checkout.css.sass index 77feb9c44e..086678adb3 100644 --- a/app/assets/stylesheets/darkswarm/checkout.css.sass +++ b/app/assets/stylesheets/darkswarm/checkout.css.sass @@ -2,6 +2,11 @@ @import branding @import animations +.order-summary + background-color: #e1f0f5 + padding: 1em + margin-bottom: 0.5em + checkout display: block @@ -55,7 +60,6 @@ checkout text-align: left // Logic to swap out up / down accordion icons - //Foundation overrides dd > a @include csstrans From db2e5e755895d52f4617634b87767e68b3713d2c Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 17:19:57 +1100 Subject: [PATCH 127/384] Add an extra class helper --- app/assets/stylesheets/darkswarm/typography.css.sass | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index a3f7085675..a0236d9337 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -26,6 +26,10 @@ a text-decoration: none color: $clr-brick-bright +.text-big + font-size: 1.5rem + font-weight: 300 + small, .small font-size: 0.75rem From 5b6bff1691f8eb02b1d404b599bb4bbb07168c9f Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 17:20:05 +1100 Subject: [PATCH 128/384] Removing bad nasty overrides which are confusing and bad. and nasty. --- app/overrides/order_item_description.rb | 5 ----- app/views/spree/orders/_order_item_description.html.haml | 3 --- 2 files changed, 8 deletions(-) delete mode 100644 app/overrides/order_item_description.rb delete mode 100644 app/views/spree/orders/_order_item_description.html.haml diff --git a/app/overrides/order_item_description.rb b/app/overrides/order_item_description.rb deleted file mode 100644 index 4ec1f83a24..0000000000 --- a/app/overrides/order_item_description.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/shared/_order_details", - :replace => "[data-hook='order_item_description']", - :partial => "spree/orders/order_item_description", - :name => "order_item_description", - :original => '1729abc5f441607b09cc0d44843a8dfd660ac5e0') \ No newline at end of file diff --git a/app/views/spree/orders/_order_item_description.html.haml b/app/views/spree/orders/_order_item_description.html.haml deleted file mode 100644 index d4531e8976..0000000000 --- a/app/views/spree/orders/_order_item_description.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -%td(data-hook = "order_item_description") - %h4= item.variant.product.name - = "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? \ No newline at end of file From e6591be55c3847e53b9f47caf4b2b1650eacd528 Mon Sep 17 00:00:00 2001 From: summerscope Date: Wed, 4 Mar 2015 17:34:24 +1100 Subject: [PATCH 129/384] More tweaking for email templates for use case of Collection from hub. Need to tweak logic on Order confiramtion page. --- .../confirm_email_for_customer.html.haml | 13 +++++-------- .../order_mailer/confirm_email_for_shop.html.haml | 13 +++++-------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 5fe2764607..ba5fabf74f 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -122,26 +122,23 @@ #{@order.shipping_method.name.html_safe} - else Collection details - - - if @order.shipping_method.andand.description - %p - @order.shipping_method.description.html_safe - if @order.order_cycle.andand.pickup_time_for(@order.distributor) %h4 Ready for collection: %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + - if @order.shipping_method.andand.description + %p + %em #{@order.shipping_method.description.html_safe} + %br   + - if @order.ship_address.full_address %p %strong Collecting from: %br #{@order.ship_address.full_address} - - if @order.shipping_method.andand.description - %br - %em #{@order.shipping_method.description.html_safe} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) %p %strong Collection instructions: diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index 68ccfe099f..bddeaa6478 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -121,26 +121,23 @@ #{@order.shipping_method.name.html_safe} - else Collection details - - - if @order.shipping_method.andand.description - %p - @order.shipping_method.description.html_safe - if @order.order_cycle.andand.pickup_time_for(@order.distributor) %h4 Ready for collection: %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + - if @order.shipping_method.andand.description + %p + %em #{@order.shipping_method.description.html_safe} + %br   + - if @order.ship_address.full_address %p %strong Collecting from: %br #{@order.ship_address.full_address} - - if @order.shipping_method.andand.description - %br - %em #{@order.shipping_method.description.html_safe} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) %p %strong Collection instructions: From be55f461d086017c54359c5a89518eec7aab6d32 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Sun, 23 Nov 2014 19:51:52 +0000 Subject: [PATCH 130/384] Report: sales tax on orders --- .../admin/reports_controller_decorator.rb | 34 ++++++++++++- app/models/spree/ability_decorator.rb | 2 +- config/routes.rb | 3 +- lib/open_food_network/sales_tax_report.rb | 49 +++++++++++++++++++ spec/features/admin/reports_spec.rb | 9 ++++ 5 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 lib/open_food_network/sales_tax_report.rb diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 655d1778dc..cb07e7245e 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -6,6 +6,7 @@ require 'open_food_network/order_grouper' require 'open_food_network/customers_report' require 'open_food_network/users_and_enterprises_report' require 'open_food_network/order_cycle_management_report' +require 'open_food_network/sales_tax_report' Spree::Admin::ReportsController.class_eval do @@ -102,6 +103,36 @@ Spree::Admin::ReportsController.class_eval do send_data csv_string, :filename => "orders_and_distributors_#{timestamp}.csv" end end + + def sales_tax + params[:q] = {} unless params[:q] + + if params[:q][:completed_at_gt].blank? + params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month + else + params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month + end + + if params[:q] && !params[:q][:completed_at_lt].blank? + params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue "" + end + params[:q][:meta_sort] ||= "completed_at.desc" + + @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) + orders = @search.result + @distributors = Enterprise.is_distributor.managed_by(spree_current_user) + + @report = OpenFoodNetwork::SalesTaxReport.new orders + unless params[:csv] + render :html => @report + else + csv_string = CSV.generate do |csv| + csv << @report.header + @report.table.each { |row| csv << row } + end + send_data csv_string, :filename => "sales_tax.csv" + end + end def bulk_coop params[:q] = {} unless params[:q] @@ -641,7 +672,8 @@ Spree::Admin::ReportsController.class_eval do :products_and_inventory => {:name => "Products & Inventory", :description => ''}, :sales_total => { :name => "Sales Total", :description => "Sales Total For All Orders" }, :users_and_enterprises => { :name => "Users & Enterprises", :description => "Enterprise Ownership & Status" }, - :order_cycle_management => {:name => "Order Cycle Management", :description => ''} + :order_cycle_management => {:name => "Order Cycle Management", :description => ''}, + :sales_tax => { :name => "Sales Tax", :description => "Sales Tax For Orders" } } # Return only reports the user is authorized to view. reports.select { |action| can? action, :report } diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 9bff908346..145d8310de 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -153,7 +153,7 @@ class AbilityDecorator end # Reports page - can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], :report + can [:admin, :index, :customers, :group_buys, :bulk_coop, :sales_tax, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], :report end diff --git a/config/routes.rb b/config/routes.rb index e7f9f2bba0..401ecbeaf3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -123,7 +123,8 @@ Spree::Core::Engine.routes.prepend do match '/admin/reports/bulk_coop' => 'admin/reports#bulk_coop', :as => "bulk_coop_admin_reports", :via => [:get, :post] match '/admin/reports/payments' => 'admin/reports#payments', :as => "payments_admin_reports", :via => [:get, :post] match '/admin/reports/orders_and_fulfillment' => 'admin/reports#orders_and_fulfillment', :as => "orders_and_fulfillment_admin_reports", :via => [:get, :post] - match '/admin/reports/users_and_enterprises' => 'admin/reports#users_and_enterprises', :as => "users_and_enterprises_admin_reports", :via => [:get, :post] + match '/admin/reports/users_and_enterprises' => 'admin/reports#users_and_enterprises', :as => "users_and_enterprises_admin_reports", :via => [:get, :post] + match '/admin/reports/sales_tax' => 'admin/reports#sales_tax', :as => "sales_tax_admin_reports", :via => [:get, :post] match '/admin/products/bulk_edit' => 'admin/products#bulk_edit', :as => "bulk_edit_admin_products" match '/admin/orders/bulk_management' => 'admin/orders#bulk_management', :as => "admin_bulk_order_management" match '/admin/reports/products_and_inventory' => 'admin/reports#products_and_inventory', :as => "products_and_inventory_admin_reports", :via => [:get, :post] diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb new file mode 100644 index 0000000000..b3aa8b5419 --- /dev/null +++ b/lib/open_food_network/sales_tax_report.rb @@ -0,0 +1,49 @@ + +module OpenFoodNetwork + class SalesTaxReport + + def initialize orders + @orders = orders + end + + def header + ["Order number", "Date", "Items", "Items total", "Taxable Items Total", "Sales Tax", + "Delivery Charge", "Tax on Delivery", "Total Tax", "Customer", "Distributor"] + end + + def table + sales_tax_details = [] + @orders.each do |order| + totals = {"items" => 0, "items_total" => 0.0, "taxable_total" => 0.0, "sales_tax" => 0.0} + + order.line_items.each do |line_item| + totals["items"] += line_item.quantity + totals["items_total"] += line_item.price * line_item.quantity + + tax_rate = Spree::TaxRate.find_by_tax_category_id(line_item.variant.product.tax_category_id).andand.amount + + if tax_rate != nil && tax_rate != 0 + totals["taxable_total"] += (line_item.price * line_item.quantity) + totals["sales_tax"] += (line_item.price * line_item.quantity) * tax_rate + end + end + + shipping_cost = order.adjustments.find_by_label("Shipping").andand.amount + shipping_cost = (shipping_cost == nil) ? 0.0 : shipping_cost + shipping_tax = (Spree::Config[:shipment_inc_vat] && shipping_cost != nil) ? shipping_cost * 0.2 : 0.0 + + #config option for charging tax on shipping fee or not? exists, need to set rate... + #calculate additional tax for shipping... + #ignore non-shipping adjustments? any potential issues? + #show payment status? other necessary/useful info? + #check which orders are pulled, and which are filtered out... maybe have a dropdown to make it explicit...? + + sales_tax_details << [order.number, order.created_at, totals["items"], totals["items_total"], + totals["taxable_total"], totals["sales_tax"], shipping_cost, shipping_tax, totals["sales_tax"] + shipping_tax, + order.bill_address.full_name, order.distributor.andand.name] + + end + sales_tax_details + end + end +end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 0524a6769e..056b9b44b8 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -98,6 +98,15 @@ feature %q{ page.should have_content 'Payment State' end + + scenario "Sales Tax report" do + login_to_admin_section + click_link 'Reports' + click_link 'Sales Tax' + + page.should have_content 'Total Tax' + page.should have_select 'distributor_id' + end describe "orders & fulfilment reports" do it "loads the report page" do From 371f966f63dc1259932d98d64ec5db2063fdd4fa Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Sun, 23 Nov 2014 19:55:03 +0000 Subject: [PATCH 131/384] sales tax view --- .../spree/admin/reports/sales_tax.html.haml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 app/views/spree/admin/reports/sales_tax.html.haml diff --git a/app/views/spree/admin/reports/sales_tax.html.haml b/app/views/spree/admin/reports/sales_tax.html.haml new file mode 100644 index 0000000000..805f0ae627 --- /dev/null +++ b/app/views/spree/admin/reports/sales_tax.html.haml @@ -0,0 +1,40 @@ += form_for @search, :url => spree.sales_tax_admin_reports_path do |s| + = label_tag nil, t(:date_range) + %br + %br + .date-range-filter + = label_tag nil, t(:start), :class => 'inline' + = s.text_field :completed_at_gt, :class => 'datepicker datepicker-from' + %span.range-divider + %i.icon-arrow-right + = s.text_field :completed_at_lt, :class => 'datepicker datepicker-to' + = label_tag nil, t(:stop), :class => 'inline' + %br + %div{"class" => "row"} + %div{"class" => "four columns alpha"} + = label_tag nil, "Distributor: " + = s.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"}) + = check_box_tag :csv + = label_tag :csv, "Download as csv" + %br + = button t(:search) +%br +%br +%table#listing_orders.index + %thead + %tr{'data-hook' => "orders_header"} + - @report.header.each do |heading| + %th=heading + %tbody + - @report.table.each do |row| + %tr + - row.each_with_index do |column, i| + -if i==0 + %td + %a{:class => "edit-order", 'href' => "/admin/orders/#{column}"} #{column} + -else + %td= column + - if @report.table.empty? + %tr + %td{:colspan => "2"}= t(:none) + From 623882a2a1aeeb0a32a3a4addfb45ec8e324732e Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Mon, 24 Nov 2014 03:01:00 +0000 Subject: [PATCH 132/384] Config option for tax rate on shipping --- app/models/spree/app_configuration_decorator.rb | 15 ++++++++------- .../edit/shipping_tax_rate.html.haml.deface | 5 +++++ lib/open_food_network/sales_tax_report.rb | 12 +++++------- 3 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index f4f4acc5ec..5ad4a9a1c5 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -1,9 +1,10 @@ Spree::AppConfiguration.class_eval do - # This file decorates the existing preferences file defined by Spree. - # It allows us to add our own global configuration variables, which - # we can allow to be modified in the UI by adding appropriate form - # elements to existing or new configuration pages. + # This file decorates the existing preferences file defined by Spree. + # It allows us to add our own global configuration variables, which + # we can allow to be modified in the UI by adding appropriate form + # elements to existing or new configuration pages. - # Tax Preferences - preference :products_require_tax_category, :boolean, default: false -end \ No newline at end of file + # Tax Preferences + preference :products_require_tax_category, :boolean, default: false + preference :shipping_tax_rate, :decimal, default: 0 +end diff --git a/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface b/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface new file mode 100644 index 0000000000..222e189d87 --- /dev/null +++ b/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface @@ -0,0 +1,5 @@ +/ insert_after "[data-hook='shipment_vat']" + +%div.field.align-center{ "data-hook" => "shipping_tax_rate" } + = number_field_tag "preferences[shipping_tax_rate]", Spree::Config[:shipping_tax_rate].to_f, in: 0.0..1.0, step: 0.01 + = label_tag nil, t(:shipping_tax_rate) \ No newline at end of file diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index b3aa8b5419..6d1945e1f5 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -30,13 +30,11 @@ module OpenFoodNetwork shipping_cost = order.adjustments.find_by_label("Shipping").andand.amount shipping_cost = (shipping_cost == nil) ? 0.0 : shipping_cost - shipping_tax = (Spree::Config[:shipment_inc_vat] && shipping_cost != nil) ? shipping_cost * 0.2 : 0.0 - - #config option for charging tax on shipping fee or not? exists, need to set rate... - #calculate additional tax for shipping... - #ignore non-shipping adjustments? any potential issues? - #show payment status? other necessary/useful info? - #check which orders are pulled, and which are filtered out... maybe have a dropdown to make it explicit...? + if Spree::Config[:shipment_inc_vat] && shipping_cost != nil + shipping_tax = shipping_cost * Spree::Config[:shipping_tax_rate] + else + shipping_tax = 0.0 + end sales_tax_details << [order.number, order.created_at, totals["items"], totals["items_total"], totals["taxable_total"], totals["sales_tax"], shipping_cost, shipping_tax, totals["sales_tax"] + shipping_tax, From bbca674937b2d332049e4e250890971576da004e Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Mon, 24 Nov 2014 12:23:48 +0000 Subject: [PATCH 133/384] Update reports_spec.rb --- spec/features/admin/reports_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 056b9b44b8..7899858d2f 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -105,7 +105,6 @@ feature %q{ click_link 'Sales Tax' page.should have_content 'Total Tax' - page.should have_select 'distributor_id' end describe "orders & fulfilment reports" do From 0f3723a923a951ff30390254026c06209271dadf Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Mon, 24 Nov 2014 23:34:02 +0000 Subject: [PATCH 134/384] Added currency symbols to sales tax report --- app/models/spree/money_decorator.rb | 7 +++++++ lib/open_food_network/sales_tax_report.rb | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 app/models/spree/money_decorator.rb diff --git a/app/models/spree/money_decorator.rb b/app/models/spree/money_decorator.rb new file mode 100644 index 0000000000..e179343bc5 --- /dev/null +++ b/app/models/spree/money_decorator.rb @@ -0,0 +1,7 @@ +Spree::Money.class_eval do + + # return the currency symbol (on it's own) for the current default currency + def self.currency_symbol + Money.new(0, Spree::Config[:currency]).symbol + end +end diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 6d1945e1f5..76b2534a66 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -7,8 +7,10 @@ module OpenFoodNetwork end def header - ["Order number", "Date", "Items", "Items total", "Taxable Items Total", "Sales Tax", - "Delivery Charge", "Tax on Delivery", "Total Tax", "Customer", "Distributor"] + currency_symbol = Spree::Money.currency_symbol + ["Order number", "Date", "Items", "Items total (#{currency_symbol})", "Taxable Items Total (#{currency_symbol})", + "Sales Tax", "Delivery Charge (#{currency_symbol})", "Tax on Delivery (#{currency_symbol})", + "Total Tax (#{currency_symbol})", "Customer", "Distributor"] end def table From 3f61a5412c74211732aa2be033b48b9eccbe6090 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Thu, 27 Nov 2014 12:45:53 +0000 Subject: [PATCH 135/384] Spec file attempt... --- spec/features/admin/reports_spec.rb | 75 +++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 7899858d2f..2d76405e87 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -99,12 +99,77 @@ feature %q{ page.should have_content 'Payment State' end - scenario "Sales Tax report" do - login_to_admin_section - click_link 'Reports' - click_link 'Sales Tax' + describe "Sales Tax report" do + include AuthenticationWorkflow + include WebHelper + let(:user1) do + create_enterprise_user(enterprises: [ + create(:distributor_enterprise) + ]) + end + let(:user2) do + create_enterprise_user(enterprises: [ + create(:distributor_enterprise) + ]) + end + let(:tax_category1) { create(:tax_category) } + let(:tax_category2) { create(:tax_category) } + let(:country) { create(:country, :name => "Spec Republic") } + let(:state) { create(:state, :country => country, :name => "Specville") } + #let(:zone) { create(:zone, :name => "Test Tax Zone") } + #let(:zoneable) { create(:zoneable, :zoneable => country) } + let(:tax_rate1) { create(:tax_rate, :amount => 0.0, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category1, :zone => zone) } + let(:tax_rate2) { create(:tax_rate, :amount => 0.2, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category2, :zone => zone) } + + let(:product1) { create(:product, :tax_category => tax_category1) } + let(:product2) { create(:product, :tax_category => tax_category2) } + let(:variant1) { create(:variant, price: 12.54, :product => product1) } + let(:variant2) { create(:variant, price: 500.15, :product => product2) } + + let(:product_distribution1) { create(:product_distribution, :product => variant1.product, :distributor => user1.enterprises.first) } + let(:product_distribution2) { create(:product_distribution, :product => variant2.product, :distributor => user2.enterprises.first) } + let(:address) { create(:address) } # + let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } + let(:order1) { create(:order, :distributor => user1.enterprises.first, :shipping_method => shipping_method, bill_address: address, ship_address: address) } + let(:line_item1) { create(:line_item, :variant => variant1, :price => 12.54, :quantity => 1, :order => order1) } + let(:line_item2) { create(:line_item, :variant => variant2, :price => 500.15, :quantity => 3, :order => order1) } + let(:adjustment) { create(:adjustment, :adjustable => order1, :label => "Shipping", :amount => 100.55) } + + before do + Spree::Config.shipment_inc_vat = true + Spree::Config.shipping_tax_rate = 0.2 + order1.finalize! + login_to_admin_as user1 + click_link "Reports" + click_link "Sales Tax" + end + + it "displays the report" do + page.should have_content "#{user1.enterprises.first.name}" #listed in distributors dropdown + page.should_not have_content "#{user2.enterprises.first.name}" #not listed + + select user1.enterprises.first.name, from: 'q_distributor_id_eq' + click_button 'Search' + + page.should have_content "#{order1.number}" + end - page.should have_content 'Total Tax' + it "calculates sales tax on orders" do + select user1.enterprises.first.name, from: 'q_distributor_id_eq' + click_button 'Search' + + page.should have_content "1512.99" #items total + page.should have_content "1500.45" #taxable items total + page.should have_content "300.09" #sales tax + end + + it "calculates shipping tax on orders" do + select user1.enterprises.first.name, from: 'q_distributor_id_eq' + click_button 'Search' + + page.should have_content "100.55" #shipping cost + page.should have_content "20.11" #shipping tax + end end describe "orders & fulfilment reports" do From d194e74edaafe47b882888e50c93bb7404dd98cf Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Thu, 27 Nov 2014 13:38:52 +0000 Subject: [PATCH 136/384] Rohan's suggested changes --- .../admin/reports_controller_decorator.rb | 2 +- .../spree/admin/reports/sales_tax.html.haml | 2 +- lib/open_food_network/sales_tax_report.rb | 18 +++++++++--------- spec/models/spree/ability_spec.rb | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index cb07e7245e..931e8bf9ff 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -76,7 +76,7 @@ Spree::Admin::ReportsController.class_eval do end def orders_and_distributors - params[:q] = {} unless params[:q] + params[:q] ||= {} if params[:q][:completed_at_gt].blank? params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month diff --git a/app/views/spree/admin/reports/sales_tax.html.haml b/app/views/spree/admin/reports/sales_tax.html.haml index 805f0ae627..9231438588 100644 --- a/app/views/spree/admin/reports/sales_tax.html.haml +++ b/app/views/spree/admin/reports/sales_tax.html.haml @@ -36,5 +36,5 @@ %td= column - if @report.table.empty? %tr - %td{:colspan => "2"}= t(:none) + %td{:colspan => "12"}= t(:none) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 76b2534a66..cb604ce702 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -9,37 +9,37 @@ module OpenFoodNetwork def header currency_symbol = Spree::Money.currency_symbol ["Order number", "Date", "Items", "Items total (#{currency_symbol})", "Taxable Items Total (#{currency_symbol})", - "Sales Tax", "Delivery Charge (#{currency_symbol})", "Tax on Delivery (#{currency_symbol})", + "Sales Tax (#{currency_symbol})", "Delivery Charge (#{currency_symbol})", "Tax on Delivery (#{currency_symbol})", "Total Tax (#{currency_symbol})", "Customer", "Distributor"] end def table sales_tax_details = [] @orders.each do |order| - totals = {"items" => 0, "items_total" => 0.0, "taxable_total" => 0.0, "sales_tax" => 0.0} + totals = {items: 0, items_total: 0.0, taxable_total: 0.0, sales_tax: 0.0} order.line_items.each do |line_item| - totals["items"] += line_item.quantity - totals["items_total"] += line_item.price * line_item.quantity + totals[:items] += line_item.quantity + totals[:items_total] += line_item.amount tax_rate = Spree::TaxRate.find_by_tax_category_id(line_item.variant.product.tax_category_id).andand.amount if tax_rate != nil && tax_rate != 0 - totals["taxable_total"] += (line_item.price * line_item.quantity) - totals["sales_tax"] += (line_item.price * line_item.quantity) * tax_rate + totals[:taxable_total] += line_item.amount + totals[:sales_tax] += line_item.amount * tax_rate end end shipping_cost = order.adjustments.find_by_label("Shipping").andand.amount - shipping_cost = (shipping_cost == nil) ? 0.0 : shipping_cost + shipping_cost = shipping_cost.nil? ? 0.0 : shipping_cost if Spree::Config[:shipment_inc_vat] && shipping_cost != nil shipping_tax = shipping_cost * Spree::Config[:shipping_tax_rate] else shipping_tax = 0.0 end - sales_tax_details << [order.number, order.created_at, totals["items"], totals["items_total"], - totals["taxable_total"], totals["sales_tax"], shipping_cost, shipping_tax, totals["sales_tax"] + shipping_tax, + sales_tax_details << [order.number, order.created_at, totals[:items], totals[:items_total], + totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, totals[:sales_tax] + shipping_tax, order.bill_address.full_name, order.distributor.andand.name] end diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 30fbbd8ab6..19006cbcd1 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -350,7 +350,7 @@ module Spree end it "should be able to read some reports" do - should have_ability([:admin, :index, :customers, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory], for: :report) + should have_ability([:admin, :index, :customers, :sales_tax, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory], for: :report) end it "should not be able to read other reports" do From 2c80be7e9e84b4b4c678adc395e97b8d649215b6 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Thu, 27 Nov 2014 14:11:21 +0000 Subject: [PATCH 137/384] Update --- spec/features/admin/reports_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 2d76405e87..ec9f756c93 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -116,7 +116,7 @@ feature %q{ let(:tax_category2) { create(:tax_category) } let(:country) { create(:country, :name => "Spec Republic") } let(:state) { create(:state, :country => country, :name => "Specville") } - #let(:zone) { create(:zone, :name => "Test Tax Zone") } + let(:zone) { create(:zone, :name => "Test Tax Zone") } #let(:zoneable) { create(:zoneable, :zoneable => country) } let(:tax_rate1) { create(:tax_rate, :amount => 0.0, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category1, :zone => zone) } let(:tax_rate2) { create(:tax_rate, :amount => 0.2, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category2, :zone => zone) } @@ -128,9 +128,9 @@ feature %q{ let(:product_distribution1) { create(:product_distribution, :product => variant1.product, :distributor => user1.enterprises.first) } let(:product_distribution2) { create(:product_distribution, :product => variant2.product, :distributor => user2.enterprises.first) } - let(:address) { create(:address) } # - let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } - let(:order1) { create(:order, :distributor => user1.enterprises.first, :shipping_method => shipping_method, bill_address: address, ship_address: address) } + let(:address) { create(:address, :state => state, :country => country) } # + let(:shipping_method) { create(:shipping_method, :name => "Shipping", :description => "Expensive", :calculator => Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } + let(:order1) { create(:order, :distributor => user1.enterprises.first, :shipping_method => shipping_method, :bill_address => address, :ship_address => address) } let(:line_item1) { create(:line_item, :variant => variant1, :price => 12.54, :quantity => 1, :order => order1) } let(:line_item2) { create(:line_item, :variant => variant2, :price => 500.15, :quantity => 3, :order => order1) } let(:adjustment) { create(:adjustment, :adjustable => order1, :label => "Shipping", :amount => 100.55) } From e6368af75799aad9a8e040ff4af187462b8a8312 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Thu, 27 Nov 2014 14:15:13 +0000 Subject: [PATCH 138/384] Update reports_spec.rb --- spec/features/admin/reports_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index ec9f756c93..9f52b5fcc9 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -100,8 +100,6 @@ feature %q{ end describe "Sales Tax report" do - include AuthenticationWorkflow - include WebHelper let(:user1) do create_enterprise_user(enterprises: [ create(:distributor_enterprise) From 29653a5595a5c638f53a1978cd8c9578606737ab Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 14:43:24 +1100 Subject: [PATCH 139/384] Fix spec infinite recursion issue --- spec/features/admin/reports_spec.rb | 34 ++++++++++++----------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 9f52b5fcc9..92bffefebd 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -99,44 +99,38 @@ feature %q{ page.should have_content 'Payment State' end - describe "Sales Tax report" do + describe "Sales Tax report" do let(:user1) do - create_enterprise_user(enterprises: [ - create(:distributor_enterprise) - ]) + create_enterprise_user(enterprises: [create(:distributor_enterprise)]) end let(:user2) do - create_enterprise_user(enterprises: [ - create(:distributor_enterprise) - ]) + create_enterprise_user(enterprises: [create(:distributor_enterprise)]) end let(:tax_category1) { create(:tax_category) } let(:tax_category2) { create(:tax_category) } let(:country) { create(:country, :name => "Spec Republic") } let(:state) { create(:state, :country => country, :name => "Specville") } let(:zone) { create(:zone, :name => "Test Tax Zone") } - #let(:zoneable) { create(:zoneable, :zoneable => country) } - let(:tax_rate1) { create(:tax_rate, :amount => 0.0, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category1, :zone => zone) } - let(:tax_rate2) { create(:tax_rate, :amount => 0.2, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category2, :zone => zone) } - + let!(:tax_rate1) { create(:tax_rate, :amount => 0.0, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category1, :zone => zone) } + let!(:tax_rate2) { create(:tax_rate, :amount => 0.2, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category2, :zone => zone) } + let(:product1) { create(:product, :tax_category => tax_category1) } let(:product2) { create(:product, :tax_category => tax_category2) } let(:variant1) { create(:variant, price: 12.54, :product => product1) } let(:variant2) { create(:variant, price: 500.15, :product => product2) } - - let(:product_distribution1) { create(:product_distribution, :product => variant1.product, :distributor => user1.enterprises.first) } - let(:product_distribution2) { create(:product_distribution, :product => variant2.product, :distributor => user2.enterprises.first) } - let(:address) { create(:address, :state => state, :country => country) } # + + let(:address) { create(:address, :state => state, :country => country) } let(:shipping_method) { create(:shipping_method, :name => "Shipping", :description => "Expensive", :calculator => Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } - let(:order1) { create(:order, :distributor => user1.enterprises.first, :shipping_method => shipping_method, :bill_address => address, :ship_address => address) } - let(:line_item1) { create(:line_item, :variant => variant1, :price => 12.54, :quantity => 1, :order => order1) } - let(:line_item2) { create(:line_item, :variant => variant2, :price => 500.15, :quantity => 3, :order => order1) } - let(:adjustment) { create(:adjustment, :adjustable => order1, :label => "Shipping", :amount => 100.55) } - + let(:order1) { create(:order, :distributor => user1.enterprises.first, :shipping_method => shipping_method, :bill_address => address) } + let!(:line_item1) { create(:line_item, :variant => variant1, :price => 12.54, :quantity => 1, :order => order1) } + let!(:line_item2) { create(:line_item, :variant => variant2, :price => 500.15, :quantity => 3, :order => order1) } + let!(:adjustment) { create(:adjustment, :adjustable => order1, :label => "Shipping", :amount => 100.55) } + before do Spree::Config.shipment_inc_vat = true Spree::Config.shipping_tax_rate = 0.2 order1.finalize! + login_to_admin_as user1 click_link "Reports" click_link "Sales Tax" From 0212351d32e26b0c8bd86210a993c140cb1cb599 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 14:48:06 +1100 Subject: [PATCH 140/384] Tighten test --- spec/features/admin/reports_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 92bffefebd..78f3b238b8 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -137,9 +137,9 @@ feature %q{ end it "displays the report" do - page.should have_content "#{user1.enterprises.first.name}" #listed in distributors dropdown - page.should_not have_content "#{user2.enterprises.first.name}" #not listed - + page.should have_select 'q_distributor_id_eq', with_options: [user1.enterprises.first.name] + page.should_not have_select 'q_distributor_id_eq', with_options: [user2.enterprises.first.name] + select user1.enterprises.first.name, from: 'q_distributor_id_eq' click_button 'Search' From 3aa30af199279b0c86015bad9991c849f3da6d20 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 14:55:19 +1100 Subject: [PATCH 141/384] Remove unneeded objects from spec setup --- spec/features/admin/reports_spec.rb | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 78f3b238b8..cc87a98c9f 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -108,22 +108,16 @@ feature %q{ end let(:tax_category1) { create(:tax_category) } let(:tax_category2) { create(:tax_category) } - let(:country) { create(:country, :name => "Spec Republic") } - let(:state) { create(:state, :country => country, :name => "Specville") } - let(:zone) { create(:zone, :name => "Test Tax Zone") } - let!(:tax_rate1) { create(:tax_rate, :amount => 0.0, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category1, :zone => zone) } - let!(:tax_rate2) { create(:tax_rate, :amount => 0.2, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category2, :zone => zone) } + let!(:tax_rate1) { create(:tax_rate, :amount => 0.0, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category1) } + let!(:tax_rate2) { create(:tax_rate, :amount => 0.2, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category2) } - let(:product1) { create(:product, :tax_category => tax_category1) } - let(:product2) { create(:product, :tax_category => tax_category2) } - let(:variant1) { create(:variant, price: 12.54, :product => product1) } - let(:variant2) { create(:variant, price: 500.15, :product => product2) } + let(:product1) { create(:product, price: 12.54, :tax_category => tax_category1) } + let(:product2) { create(:product, price: 500.15, :tax_category => tax_category2) } - let(:address) { create(:address, :state => state, :country => country) } let(:shipping_method) { create(:shipping_method, :name => "Shipping", :description => "Expensive", :calculator => Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } - let(:order1) { create(:order, :distributor => user1.enterprises.first, :shipping_method => shipping_method, :bill_address => address) } - let!(:line_item1) { create(:line_item, :variant => variant1, :price => 12.54, :quantity => 1, :order => order1) } - let!(:line_item2) { create(:line_item, :variant => variant2, :price => 500.15, :quantity => 3, :order => order1) } + let(:order1) { create(:order, :distributor => user1.enterprises.first, :shipping_method => shipping_method, :bill_address => create(:address)) } + let!(:line_item1) { create(:line_item, :variant => product1.master, :price => 12.54, :quantity => 1, :order => order1) } + let!(:line_item2) { create(:line_item, :variant => product2.master, :price => 500.15, :quantity => 3, :order => order1) } let!(:adjustment) { create(:adjustment, :adjustable => order1, :label => "Shipping", :amount => 100.55) } before do From 2b9fef6aec20d5378f15099d9364160027c6fbdb Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 14:57:39 +1100 Subject: [PATCH 142/384] Convert to Ruby 1.9 hash syntax --- spec/features/admin/reports_spec.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index cc87a98c9f..4e2e372225 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -108,17 +108,17 @@ feature %q{ end let(:tax_category1) { create(:tax_category) } let(:tax_category2) { create(:tax_category) } - let!(:tax_rate1) { create(:tax_rate, :amount => 0.0, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category1) } - let!(:tax_rate2) { create(:tax_rate, :amount => 0.2, :calculator => Spree::Calculator::DefaultTax.new, :tax_category => tax_category2) } + let!(:tax_rate1) { create(:tax_rate, amount: 0.0, calculator: Spree::Calculator::DefaultTax.new, tax_category: tax_category1) } + let!(:tax_rate2) { create(:tax_rate, amount: 0.2, calculator: Spree::Calculator::DefaultTax.new, tax_category: tax_category2) } - let(:product1) { create(:product, price: 12.54, :tax_category => tax_category1) } - let(:product2) { create(:product, price: 500.15, :tax_category => tax_category2) } + let(:product1) { create(:product, price: 12.54, tax_category: tax_category1) } + let(:product2) { create(:product, price: 500.15, tax_category: tax_category2) } - let(:shipping_method) { create(:shipping_method, :name => "Shipping", :description => "Expensive", :calculator => Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } - let(:order1) { create(:order, :distributor => user1.enterprises.first, :shipping_method => shipping_method, :bill_address => create(:address)) } - let!(:line_item1) { create(:line_item, :variant => product1.master, :price => 12.54, :quantity => 1, :order => order1) } - let!(:line_item2) { create(:line_item, :variant => product2.master, :price => 500.15, :quantity => 3, :order => order1) } - let!(:adjustment) { create(:adjustment, :adjustable => order1, :label => "Shipping", :amount => 100.55) } + let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } + let(:order1) { create(:order, distributor: user1.enterprises.first, shipping_method: shipping_method, bill_address: create(:address)) } + let!(:line_item1) { create(:line_item, variant: product1.master, price: 12.54, quantity: 1, order: order1) } + let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } + let!(:adjustment) { create(:adjustment, adjustable: order1, label: "Shipping", amount: 100.55) } before do Spree::Config.shipment_inc_vat = true From 0b636c1d89fa0db25ca32ad508901477b76cd29e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 15:04:48 +1100 Subject: [PATCH 143/384] Combine spec cases for faster runtime --- spec/features/admin/reports_spec.rb | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 4e2e372225..93b0495ef7 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -130,29 +130,24 @@ feature %q{ click_link "Sales Tax" end - it "displays the report" do + it "reports" do + # Then it should give me access only to managed enterprises page.should have_select 'q_distributor_id_eq', with_options: [user1.enterprises.first.name] page.should_not have_select 'q_distributor_id_eq', with_options: [user2.enterprises.first.name] + # When I filter to just one distributor select user1.enterprises.first.name, from: 'q_distributor_id_eq' click_button 'Search' - + + # Then I should see the relevant order page.should have_content "#{order1.number}" - end - it "calculates sales tax on orders" do - select user1.enterprises.first.name, from: 'q_distributor_id_eq' - click_button 'Search' - - page.should have_content "1512.99" #items total - page.should have_content "1500.45" #taxable items total - page.should have_content "300.09" #sales tax - end + # And the totals and sales tax should be correct + page.should have_content "1512.99" # items total + page.should have_content "1500.45" # taxable items total + page.should have_content "300.09" # sales tax - it "calculates shipping tax on orders" do - select user1.enterprises.first.name, from: 'q_distributor_id_eq' - click_button 'Search' - + # And the shipping cost and tax should be correct page.should have_content "100.55" #shipping cost page.should have_content "20.11" #shipping tax end From cc7d6cde1d989f4c6109f3908519b7330209fe14 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 12:35:37 +1100 Subject: [PATCH 144/384] Shorter syntax --- app/controllers/spree/admin/reports_controller_decorator.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 931e8bf9ff..68048ee870 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -105,7 +105,7 @@ Spree::Admin::ReportsController.class_eval do end def sales_tax - params[:q] = {} unless params[:q] + params[:q] ||= {} if params[:q][:completed_at_gt].blank? params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month @@ -135,7 +135,7 @@ Spree::Admin::ReportsController.class_eval do end def bulk_coop - params[:q] = {} unless params[:q] + params[:q] ||= {} if params[:q][:completed_at_gt].blank? params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month @@ -288,7 +288,7 @@ Spree::Admin::ReportsController.class_eval do end def payments - params[:q] = {} unless params[:q] + params[:q] ||= {} if params[:q][:completed_at_gt].blank? params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month From 67c77cea81e2771f15da17663a880a7396236f89 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 12:41:42 +1100 Subject: [PATCH 145/384] Tidy up haml --- .../edit/shipping_tax_rate.html.haml.deface | 2 +- .../spree/admin/reports/sales_tax.html.haml | 34 +++++++------------ 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface b/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface index 222e189d87..b378ba84a6 100644 --- a/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface +++ b/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface @@ -1,5 +1,5 @@ / insert_after "[data-hook='shipment_vat']" -%div.field.align-center{ "data-hook" => "shipping_tax_rate" } +.field.align-center{ "data-hook" => "shipping_tax_rate" } = number_field_tag "preferences[shipping_tax_rate]", Spree::Config[:shipping_tax_rate].to_f, in: 0.0..1.0, step: 0.01 = label_tag nil, t(:shipping_tax_rate) \ No newline at end of file diff --git a/app/views/spree/admin/reports/sales_tax.html.haml b/app/views/spree/admin/reports/sales_tax.html.haml index 9231438588..a7b3d9275d 100644 --- a/app/views/spree/admin/reports/sales_tax.html.haml +++ b/app/views/spree/admin/reports/sales_tax.html.haml @@ -1,40 +1,32 @@ -= form_for @search, :url => spree.sales_tax_admin_reports_path do |s| - = label_tag nil, t(:date_range) - %br - %br - .date-range-filter - = label_tag nil, t(:start), :class => 'inline' - = s.text_field :completed_at_gt, :class => 'datepicker datepicker-from' - %span.range-divider - %i.icon-arrow-right - = s.text_field :completed_at_lt, :class => 'datepicker datepicker-to' - = label_tag nil, t(:stop), :class => 'inline' - %br - %div{"class" => "row"} - %div{"class" => "four columns alpha"} - = label_tag nil, "Distributor: " - = s.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"}) += form_for @search, :url => spree.sales_tax_admin_reports_path do |f| + = render 'date_range_form', f: f + + .row + .four.columns.alpha + = label_tag nil, "Distributor:" + = f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"}) = check_box_tag :csv = label_tag :csv, "Download as csv" %br = button t(:search) + %br %br %table#listing_orders.index %thead %tr{'data-hook' => "orders_header"} - @report.header.each do |heading| - %th=heading + %th= heading %tbody - @report.table.each do |row| %tr - row.each_with_index do |column, i| - -if i==0 + - if i == 0 %td - %a{:class => "edit-order", 'href' => "/admin/orders/#{column}"} #{column} - -else + %a.edit-order{'href' => "/admin/orders/#{column}"}= column + - else %td= column - if @report.table.empty? %tr - %td{:colspan => "12"}= t(:none) + %td{:colspan => @report.header.count}= t(:none) From 89199ef30af849098afd2a28c5dc5a552d645ac8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 12:46:41 +1100 Subject: [PATCH 146/384] Use map instead of each and var --- lib/open_food_network/sales_tax_report.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index cb604ce702..75a1764551 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -14,8 +14,7 @@ module OpenFoodNetwork end def table - sales_tax_details = [] - @orders.each do |order| + @orders.map do |order| totals = {items: 0, items_total: 0.0, taxable_total: 0.0, sales_tax: 0.0} order.line_items.each do |line_item| @@ -38,12 +37,10 @@ module OpenFoodNetwork shipping_tax = 0.0 end - sales_tax_details << [order.number, order.created_at, totals[:items], totals[:items_total], - totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, totals[:sales_tax] + shipping_tax, - order.bill_address.full_name, order.distributor.andand.name] - + [order.number, order.created_at, totals[:items], totals[:items_total], + totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, totals[:sales_tax] + shipping_tax, + order.bill_address.full_name, order.distributor.andand.name] end - sales_tax_details end end end From 3b4d73760b5cc49c82b774dc8bf4c3336bae9ed5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 14:12:18 +1100 Subject: [PATCH 147/384] Break up sales tax report into methods --- lib/open_food_network/sales_tax_report.rb | 62 +++++++++++++++-------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 75a1764551..86a46187be 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -15,32 +15,52 @@ module OpenFoodNetwork def table @orders.map do |order| - totals = {items: 0, items_total: 0.0, taxable_total: 0.0, sales_tax: 0.0} - - order.line_items.each do |line_item| - totals[:items] += line_item.quantity - totals[:items_total] += line_item.amount - - tax_rate = Spree::TaxRate.find_by_tax_category_id(line_item.variant.product.tax_category_id).andand.amount - - if tax_rate != nil && tax_rate != 0 - totals[:taxable_total] += line_item.amount - totals[:sales_tax] += line_item.amount * tax_rate - end - end - - shipping_cost = order.adjustments.find_by_label("Shipping").andand.amount - shipping_cost = shipping_cost.nil? ? 0.0 : shipping_cost - if Spree::Config[:shipment_inc_vat] && shipping_cost != nil - shipping_tax = shipping_cost * Spree::Config[:shipping_tax_rate] - else - shipping_tax = 0.0 - end + totals = totals_of order.line_items + shipping_cost = shipping_cost_for order + shipping_tax = shipping_tax_on shipping_cost [order.number, order.created_at, totals[:items], totals[:items_total], totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, totals[:sales_tax] + shipping_tax, order.bill_address.full_name, order.distributor.andand.name] end end + + + private + + def totals_of(line_items) + totals = {items: 0, items_total: 0.0, taxable_total: 0.0, sales_tax: 0.0} + + line_items.each do |line_item| + totals[:items] += line_item.quantity + totals[:items_total] += line_item.amount + + tax_rate = tax_rate_on line_item + + if tax_rate != nil && tax_rate != 0 + totals[:taxable_total] += line_item.amount + totals[:sales_tax] += line_item.amount * tax_rate + end + end + + totals + end + + def shipping_cost_for(order) + shipping_cost = order.adjustments.find_by_label("Shipping").andand.amount + shipping_cost = shipping_cost.nil? ? 0.0 : shipping_cost + end + + def shipping_tax_on(shipping_cost) + if Spree::Config[:shipment_inc_vat] && shipping_cost != nil + shipping_cost * Spree::Config[:shipping_tax_rate] + else + 0.0 + end + end + + def tax_rate_on(line_item) + Spree::TaxRate.find_by_tax_category_id(line_item.variant.product.tax_category_id).andand.amount + end end end From 10e5d09416fd1f0625b58146334cc54128dfb339 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 8 Jan 2015 14:15:57 +1100 Subject: [PATCH 148/384] Use ReportsHelper for currency_symbol --- lib/open_food_network/sales_tax_report.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 86a46187be..2579f91e59 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -1,13 +1,12 @@ - module OpenFoodNetwork class SalesTaxReport + include Spree::ReportsHelper def initialize orders @orders = orders end def header - currency_symbol = Spree::Money.currency_symbol ["Order number", "Date", "Items", "Items total (#{currency_symbol})", "Taxable Items Total (#{currency_symbol})", "Sales Tax (#{currency_symbol})", "Delivery Charge (#{currency_symbol})", "Tax on Delivery (#{currency_symbol})", "Total Tax (#{currency_symbol})", "Customer", "Distributor"] From 27a730ef6c35d8973b6dcc8d9435931c5209cc39 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 9 Jan 2015 15:05:25 +1100 Subject: [PATCH 149/384] Add spec for sales tax report totals calcs --- lib/open_food_network/sales_tax_report.rb | 2 +- .../sales_tax_report_spec.rb | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 spec/lib/open_food_network/sales_tax_report_spec.rb diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 2579f91e59..f5755210c1 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -38,7 +38,7 @@ module OpenFoodNetwork if tax_rate != nil && tax_rate != 0 totals[:taxable_total] += line_item.amount - totals[:sales_tax] += line_item.amount * tax_rate + totals[:sales_tax] += (line_item.amount * tax_rate / (1 + tax_rate)).round(2) end end diff --git a/spec/lib/open_food_network/sales_tax_report_spec.rb b/spec/lib/open_food_network/sales_tax_report_spec.rb new file mode 100644 index 0000000000..32354dfa38 --- /dev/null +++ b/spec/lib/open_food_network/sales_tax_report_spec.rb @@ -0,0 +1,50 @@ +require 'open_food_network/sales_tax_report' + +module OpenFoodNetwork + describe SalesTaxReport do + describe "calculating totals for line items" do + let(:li1) { double(:line_item, quantity: 1, amount: 12) } + let(:li2) { double(:line_item, quantity: 2, amount: 24) } + let(:report) { SalesTaxReport.new(nil) } + let(:totals) { report.send(:totals_of, [li1, li2]) } + + before do + report.stub(:tax_rate_on) { 0.2 } + end + + it "calculates total quantity" do + totals[:items].should == 3 + end + + it "calculates total price" do + totals[:items_total].should == 36 + end + + it "calculates the taxable total price" do + totals[:taxable_total].should == 36 + end + + it "calculates sales tax" do + totals[:sales_tax].should == 6 + end + + context "when there is no tax on a line item" do + before do + report.stub(:tax_rate_on) { nil } + end + + it "does not appear in taxable total" do + totals[:taxable_total].should == 0 + end + + it "still appears on items total" do + totals[:items_total].should == 36 + end + + it "does not register sales tax" do + totals[:sales_tax].should == 0 + end + end + end + end +end From 11f59c9f59189ed04adf47689df17cdb5f8a3d9b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 9 Jan 2015 15:21:54 +1100 Subject: [PATCH 150/384] Add spec for calculating shipping tax. Fix incorrect formula. --- lib/open_food_network/sales_tax_report.rb | 14 +++++++++--- .../sales_tax_report_spec.rb | 22 ++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index f5755210c1..7687b22ef7 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -51,15 +51,23 @@ module OpenFoodNetwork end def shipping_tax_on(shipping_cost) - if Spree::Config[:shipment_inc_vat] && shipping_cost != nil - shipping_cost * Spree::Config[:shipping_tax_rate] + if shipment_inc_vat && shipping_cost.present? + (shipping_cost * shipping_tax_rate / (1 + shipping_tax_rate)).round(2) else - 0.0 + 0 end end def tax_rate_on(line_item) Spree::TaxRate.find_by_tax_category_id(line_item.variant.product.tax_category_id).andand.amount end + + def shipment_inc_vat + Spree::Config.shipment_inc_vat + end + + def shipping_tax_rate + Spree::Config.shipping_tax_rate + end end end diff --git a/spec/lib/open_food_network/sales_tax_report_spec.rb b/spec/lib/open_food_network/sales_tax_report_spec.rb index 32354dfa38..234318e9df 100644 --- a/spec/lib/open_food_network/sales_tax_report_spec.rb +++ b/spec/lib/open_food_network/sales_tax_report_spec.rb @@ -2,10 +2,11 @@ require 'open_food_network/sales_tax_report' module OpenFoodNetwork describe SalesTaxReport do + let(:report) { SalesTaxReport.new(nil) } + describe "calculating totals for line items" do let(:li1) { double(:line_item, quantity: 1, amount: 12) } let(:li2) { double(:line_item, quantity: 2, amount: 24) } - let(:report) { SalesTaxReport.new(nil) } let(:totals) { report.send(:totals_of, [li1, li2]) } before do @@ -46,5 +47,24 @@ module OpenFoodNetwork end end end + + describe "calculating the shipping tax on a shipping cost" do + it "returns zero when shipping does not include VAT" do + report.stub(:shipment_inc_vat) { false } + report.send(:shipping_tax_on, 12).should == 0 + end + + it "returns zero when no shipping cost is passed" do + report.stub(:shipment_inc_vat) { true } + report.send(:shipping_tax_on, nil).should == 0 + end + + + it "returns the tax included in the price otherwise" do + report.stub(:shipment_inc_vat) { true } + report.stub(:shipping_tax_rate) { 0.2 } + report.send(:shipping_tax_on, 12).should == 2 + end + end end end From 54894fb2222e925e3adafbe972323383741f6a74 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 9 Jan 2015 15:24:55 +1100 Subject: [PATCH 151/384] Update spec with correct tax amounts --- spec/features/admin/reports_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 93b0495ef7..c74b079ae5 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -145,11 +145,11 @@ feature %q{ # And the totals and sales tax should be correct page.should have_content "1512.99" # items total page.should have_content "1500.45" # taxable items total - page.should have_content "300.09" # sales tax + page.should have_content "250.08" # sales tax # And the shipping cost and tax should be correct page.should have_content "100.55" #shipping cost - page.should have_content "20.11" #shipping tax + page.should have_content "16.76" #shipping tax end end From ec22f4c09f1c3802a7f2b865e128946fd4f83733 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 13 Jan 2015 13:46:09 +1100 Subject: [PATCH 152/384] Sales tax report pulls sales tax from adjustments instead of recalculating it at report-time --- app/models/spree/adjustment_decorator.rb | 1 + lib/open_food_network/sales_tax_report.rb | 10 +++++----- spec/features/admin/reports_spec.rb | 13 ++++++++----- spec/lib/open_food_network/sales_tax_report_spec.rb | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/app/models/spree/adjustment_decorator.rb b/app/models/spree/adjustment_decorator.rb index 2c5bb0be0a..0c6ce0b63d 100644 --- a/app/models/spree/adjustment_decorator.rb +++ b/app/models/spree/adjustment_decorator.rb @@ -3,5 +3,6 @@ module Spree has_one :metadata, class_name: 'AdjustmentMetadata', dependent: :destroy scope :enterprise_fee, where(originator_type: 'EnterpriseFee') + scope :included_tax, where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::LineItem') end end diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 7687b22ef7..3920773d06 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -34,11 +34,11 @@ module OpenFoodNetwork totals[:items] += line_item.quantity totals[:items_total] += line_item.amount - tax_rate = tax_rate_on line_item + sales_tax = tax_included_in line_item - if tax_rate != nil && tax_rate != 0 + if sales_tax > 0 totals[:taxable_total] += line_item.amount - totals[:sales_tax] += (line_item.amount * tax_rate / (1 + tax_rate)).round(2) + totals[:sales_tax] += sales_tax end end @@ -58,8 +58,8 @@ module OpenFoodNetwork end end - def tax_rate_on(line_item) - Spree::TaxRate.find_by_tax_category_id(line_item.variant.product.tax_category_id).andand.amount + def tax_included_in(line_item) + line_item.adjustments.included_tax.sum &:amount end def shipment_inc_vat diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index c74b079ae5..2ad4e34f84 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -99,7 +99,7 @@ feature %q{ page.should have_content 'Payment State' end - describe "Sales Tax report" do + describe "Sales tax report" do let(:user1) do create_enterprise_user(enterprises: [create(:distributor_enterprise)]) end @@ -118,7 +118,9 @@ feature %q{ let(:order1) { create(:order, distributor: user1.enterprises.first, shipping_method: shipping_method, bill_address: create(:address)) } let!(:line_item1) { create(:line_item, variant: product1.master, price: 12.54, quantity: 1, order: order1) } let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } - let!(:adjustment) { create(:adjustment, adjustable: order1, label: "Shipping", amount: 100.55) } + + let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", amount: 100.55) } + let!(:adj_li2_tax) { create(:adjustment, adjustable: line_item2, source: line_item2, originator: tax_rate2, label: "RandomTax", amount: 123.00) } before do Spree::Config.shipment_inc_vat = true @@ -145,11 +147,12 @@ feature %q{ # And the totals and sales tax should be correct page.should have_content "1512.99" # items total page.should have_content "1500.45" # taxable items total - page.should have_content "250.08" # sales tax + page.should have_content "123.0" # sales tax (from adj_li2_tax, not calculated on the fly) + page.should_not have_content "250.08" # the number that would have been calculated on the fly # And the shipping cost and tax should be correct - page.should have_content "100.55" #shipping cost - page.should have_content "16.76" #shipping tax + page.should have_content "100.55" # shipping cost + page.should have_content "16.76" # shipping tax # TODO: do not calculate on the fly end end diff --git a/spec/lib/open_food_network/sales_tax_report_spec.rb b/spec/lib/open_food_network/sales_tax_report_spec.rb index 234318e9df..cf00f650ff 100644 --- a/spec/lib/open_food_network/sales_tax_report_spec.rb +++ b/spec/lib/open_food_network/sales_tax_report_spec.rb @@ -10,7 +10,7 @@ module OpenFoodNetwork let(:totals) { report.send(:totals_of, [li1, li2]) } before do - report.stub(:tax_rate_on) { 0.2 } + report.stub(:tax_included_in).and_return(2, 4) end it "calculates total quantity" do @@ -31,7 +31,7 @@ module OpenFoodNetwork context "when there is no tax on a line item" do before do - report.stub(:tax_rate_on) { nil } + report.stub(:tax_included_in) { 0 } end it "does not appear in taxable total" do From dd61034908c7ea50f426af4af6376a32cb7573a1 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 13 Jan 2015 14:47:13 +1100 Subject: [PATCH 153/384] Fix fractional cents appearing on sales tax report totals --- lib/open_food_network/sales_tax_report.rb | 4 ++++ spec/lib/open_food_network/sales_tax_report_spec.rb | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 3920773d06..d5b69011d2 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -42,6 +42,10 @@ module OpenFoodNetwork end end + totals.each_pair do |k, v| + totals[k] = totals[k].round(2) + end + totals end diff --git a/spec/lib/open_food_network/sales_tax_report_spec.rb b/spec/lib/open_food_network/sales_tax_report_spec.rb index cf00f650ff..043eac6ae7 100644 --- a/spec/lib/open_food_network/sales_tax_report_spec.rb +++ b/spec/lib/open_food_network/sales_tax_report_spec.rb @@ -21,6 +21,15 @@ module OpenFoodNetwork totals[:items_total].should == 36 end + context "when floating point math would result in fractional cents" do + let(:li1) { double(:line_item, quantity: 1, amount: 0.11) } + let(:li2) { double(:line_item, quantity: 2, amount: 0.12) } + + it "rounds to the nearest cent" do + totals[:items_total].should == 0.23 + end + end + it "calculates the taxable total price" do totals[:taxable_total].should == 36 end From 61c08997a1383732acb3b7eb4fc12b00d41761bf Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Feb 2015 22:25:53 +1100 Subject: [PATCH 154/384] EnterpriseFee has a TaxCategory --- app/models/enterprise_fee.rb | 1 + ...50225111538_add_tax_category_to_enterprise_fee.rb | 7 +++++++ db/schema.rb | 12 +++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20150225111538_add_tax_category_to_enterprise_fee.rb diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 6270f77b7a..23ea158181 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -1,5 +1,6 @@ class EnterpriseFee < ActiveRecord::Base belongs_to :enterprise + belongs_to :tax_category, class_name: 'Spree::TaxCategory', foreign_key: 'tax_category_id' has_and_belongs_to_many :order_cycles, join_table: 'coordinator_fees' has_many :exchange_fees, dependent: :destroy has_many :exchanges, through: :exchange_fees diff --git a/db/migrate/20150225111538_add_tax_category_to_enterprise_fee.rb b/db/migrate/20150225111538_add_tax_category_to_enterprise_fee.rb new file mode 100644 index 0000000000..05b5410587 --- /dev/null +++ b/db/migrate/20150225111538_add_tax_category_to_enterprise_fee.rb @@ -0,0 +1,7 @@ +class AddTaxCategoryToEnterpriseFee < ActiveRecord::Migration + def change + add_column :enterprise_fees, :tax_category_id, :integer + add_foreign_key :enterprise_fees, :spree_tax_categories, column: :tax_category_id + add_index :enterprise_fees, :tax_category_id + end +end diff --git a/db/schema.rb b/db/schema.rb index a8ac91e6d8..9301baf229 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150220035501) do +ActiveRecord::Schema.define(:version => 20150225111538) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -177,11 +177,13 @@ ActiveRecord::Schema.define(:version => 20150220035501) do t.integer "enterprise_id" t.string "fee_type" t.string "name" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "tax_category_id" end add_index "enterprise_fees", ["enterprise_id"], :name => "index_enterprise_fees_on_enterprise_id" + add_index "enterprise_fees", ["tax_category_id"], :name => "index_enterprise_fees_on_tax_category_id" create_table "enterprise_groups", :force => true do |t| t.string "name" @@ -1088,6 +1090,10 @@ ActiveRecord::Schema.define(:version => 20150220035501) do 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_fees", "spree_tax_categories", name: "enterprise_fees_tax_category_id_fk", column: "tax_category_id" + + add_foreign_key "enterprise_groups", "spree_addresses", name: "enterprise_groups_address_id_fk", column: "address_id" + add_foreign_key "enterprise_groups", "spree_users", name: "enterprise_groups_owner_id_fk", column: "owner_id" add_foreign_key "enterprise_groups", "spree_addresses", name: "enterprise_groups_address_id_fk", column: "address_id" add_foreign_key "enterprise_groups", "spree_users", name: "enterprise_groups_owner_id_fk", column: "owner_id" From 53fa71d1f334c268dbd78a2c5607fa3891faad26 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 26 Feb 2015 09:20:35 +1100 Subject: [PATCH 155/384] Admin can set enterprise fee's tax category --- app/controllers/admin/enterprise_fees_controller.rb | 1 + app/models/enterprise_fee.rb | 2 +- app/presenters/enterprise_fee_presenter.rb | 2 +- app/views/admin/enterprise_fees/index.html.haml | 2 ++ app/views/admin/enterprise_fees/index.rep | 1 + spec/features/admin/enterprise_fees_spec.rb | 12 ++++++++++-- 6 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index 085828d7b3..c4d4a621b1 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -59,6 +59,7 @@ module Admin def load_data @calculators = EnterpriseFee.calculators.sort_by(&:name) + @tax_categories = Spree::TaxCategory.order('is_default DESC, name ASC') end def collection diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 23ea158181..730bac0787 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -9,7 +9,7 @@ class EnterpriseFee < ActiveRecord::Base calculated_adjustments - attr_accessible :enterprise_id, :fee_type, :name, :calculator_type + attr_accessible :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type FEE_TYPES = %w(packing transport admin sales fundraising) PER_ORDER_CALCULATORS = ['Spree::Calculator::FlatRate', 'Spree::Calculator::FlexiRate'] diff --git a/app/presenters/enterprise_fee_presenter.rb b/app/presenters/enterprise_fee_presenter.rb index a0a6d8460a..b8f9ae4655 100644 --- a/app/presenters/enterprise_fee_presenter.rb +++ b/app/presenters/enterprise_fee_presenter.rb @@ -3,7 +3,7 @@ class EnterpriseFeePresenter @controller, @enterprise_fee, @index = controller, enterprise_fee, index end - delegate :id, :enterprise_id, :fee_type, :name, :calculator_type, :to => :enterprise_fee + delegate :id, :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type, :to => :enterprise_fee def enterprise_fee @enterprise_fee diff --git a/app/views/admin/enterprise_fees/index.html.haml b/app/views/admin/enterprise_fees/index.html.haml index ba3c3e6f4c..2237ca8a43 100644 --- a/app/views/admin/enterprise_fees/index.html.haml +++ b/app/views/admin/enterprise_fees/index.html.haml @@ -13,6 +13,7 @@ %th Enterprise %th Fee Type %th Name + %th Tax Category %th Calculator %th Calculator values %th.actions @@ -24,6 +25,7 @@ = f.ng_collection_select :enterprise_id, @enterprises, :id, :name, 'enterprise_fee.enterprise_id', :include_blank => true %td= f.ng_select :fee_type, enterprise_fee_type_options, 'enterprise_fee.fee_type' %td= f.ng_text_field :name, { placeholder: 'e.g. packing fee' } + %td= f.ng_collection_select :tax_category_id, @tax_categories, :id, :name, 'enterprise_fee.tax_category_id' %td= f.ng_collection_select :calculator_type, @calculators, :name, :description, 'enterprise_fee.calculator_type', {'class' => 'calculator_type', 'ng-model' => 'calculatorType', 'spree-ensure-calculator-preferences-match-type' => "1"} %td{'ng-bind-html-unsafe-compiled' => 'enterprise_fee.calculator_settings'} %td.actions{'spree-delete-resource' => "1"} diff --git a/app/views/admin/enterprise_fees/index.rep b/app/views/admin/enterprise_fees/index.rep index 61192ca786..8dc24b5d74 100644 --- a/app/views/admin/enterprise_fees/index.rep +++ b/app/views/admin/enterprise_fees/index.rep @@ -4,6 +4,7 @@ r.list_of :enterprise_fees, @presented_collection do r.element :enterprise_name r.element :fee_type r.element :name + r.element :tax_category_id r.element :calculator_type r.element :calculator_description r.element :calculator_settings if @include_calculators diff --git a/spec/features/admin/enterprise_fees_spec.rb b/spec/features/admin/enterprise_fees_spec.rb index 44f26deddd..36fe1b76ac 100644 --- a/spec/features/admin/enterprise_fees_spec.rb +++ b/spec/features/admin/enterprise_fees_spec.rb @@ -6,9 +6,12 @@ feature %q{ }, js: true do include AuthenticationWorkflow include WebHelper - + + let!(:tax_category_gst) { create(:tax_category, name: 'GST') } + let!(:tax_category_gst_exempt) { create(:tax_category, name: 'GST exempt') } + scenario "listing enterprise fees" do - fee = create(:enterprise_fee, name: '$0.50 / kg', fee_type: 'packing') + fee = create(:enterprise_fee, name: '$0.50 / kg', fee_type: 'packing', tax_category: tax_category_gst) amount = fee.calculator.preferred_amount login_to_admin_section @@ -18,6 +21,7 @@ feature %q{ page.should have_selector "#enterprise_fee_set_collection_attributes_0_enterprise_id" page.should have_selector "option[selected]", text: 'Packing' page.should have_selector "input[value='$0.50 / kg']" + page.should have_selector "option[selected]", text: 'GST' page.should have_selector "option[selected]", text: 'Flat Rate (per item)' page.should have_selector "input[value='#{amount}']" end @@ -35,6 +39,7 @@ feature %q{ select 'Feedme', from: 'enterprise_fee_set_collection_attributes_0_enterprise_id' select 'Admin', from: 'enterprise_fee_set_collection_attributes_0_fee_type' fill_in 'enterprise_fee_set_collection_attributes_0_name', with: 'Hello!' + select 'GST', from: 'enterprise_fee_set_collection_attributes_0_tax_category_id' select 'Flat Percent', from: 'enterprise_fee_set_collection_attributes_0_calculator_type' click_button 'Update' @@ -64,6 +69,7 @@ feature %q{ select 'Foo', from: 'enterprise_fee_set_collection_attributes_0_enterprise_id' select 'Admin', from: 'enterprise_fee_set_collection_attributes_0_fee_type' fill_in 'enterprise_fee_set_collection_attributes_0_name', with: 'Greetings!' + select 'GST exempt', from: 'enterprise_fee_set_collection_attributes_0_tax_category_id' select 'Flat Percent', from: 'enterprise_fee_set_collection_attributes_0_calculator_type' click_button 'Update' @@ -71,6 +77,7 @@ feature %q{ page.should have_selector "option[selected]", text: 'Foo' page.should have_selector "option[selected]", text: 'Admin' page.should have_selector "input[value='Greetings!']" + page.should have_selector "option[selected]", text: 'GST exempt' page.should have_selector "option[selected]", text: 'Flat Percent' end @@ -137,6 +144,7 @@ feature %q{ select distributor1.name, :from => 'enterprise_fee_set_collection_attributes_0_enterprise_id' fill_in 'enterprise_fee_set_collection_attributes_0_name', :with => 'foo' + select 'GST', from: 'enterprise_fee_set_collection_attributes_0_tax_category_id' select 'Flat Percent', :from => 'enterprise_fee_set_collection_attributes_0_calculator_type' click_button 'Update' From 9395f6c80803861141e397f3a99e7e7e0f1dd41a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 2 Mar 2015 11:01:37 +1100 Subject: [PATCH 156/384] Record the tax included in adjustments. TaxRate adjustments consist of 100% tax. --- app/models/spree/adjustment_decorator.rb | 6 ++++ app/models/spree/tax_rate_decorator.rb | 11 +++++++ ...5232938_add_included_tax_to_adjustments.rb | 5 ++++ db/schema.rb | 3 +- spec/models/spree/adjustment_spec.rb | 30 +++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 app/models/spree/tax_rate_decorator.rb create mode 100644 db/migrate/20150225232938_add_included_tax_to_adjustments.rb diff --git a/app/models/spree/adjustment_decorator.rb b/app/models/spree/adjustment_decorator.rb index 0c6ce0b63d..b9b89cde6a 100644 --- a/app/models/spree/adjustment_decorator.rb +++ b/app/models/spree/adjustment_decorator.rb @@ -4,5 +4,11 @@ module Spree scope :enterprise_fee, where(originator_type: 'EnterpriseFee') scope :included_tax, where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::LineItem') + + attr_accessible :included_tax + + def set_included_tax!(fraction) + update_attributes! included_tax: (amount * fraction).round(2) + end end end diff --git a/app/models/spree/tax_rate_decorator.rb b/app/models/spree/tax_rate_decorator.rb new file mode 100644 index 0000000000..f8a0333ce2 --- /dev/null +++ b/app/models/spree/tax_rate_decorator.rb @@ -0,0 +1,11 @@ +Spree::TaxRate.class_eval do + def adjust_with_included_tax(order) + adjust_without_included_tax(order) + + (order.adjustments.tax + order.price_adjustments).each do |a| + a.set_included_tax! 1.0 + end + end + + alias_method_chain :adjust, :included_tax +end diff --git a/db/migrate/20150225232938_add_included_tax_to_adjustments.rb b/db/migrate/20150225232938_add_included_tax_to_adjustments.rb new file mode 100644 index 0000000000..fa28ab20c7 --- /dev/null +++ b/db/migrate/20150225232938_add_included_tax_to_adjustments.rb @@ -0,0 +1,5 @@ +class AddIncludedTaxToAdjustments < ActiveRecord::Migration + def change + add_column :spree_adjustments, :included_tax, :decimal, precision: 10, scale: 2 + end +end diff --git a/db/schema.rb b/db/schema.rb index 9301baf229..cc55ad2392 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150225111538) do +ActiveRecord::Schema.define(:version => 20150225232938) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -416,6 +416,7 @@ ActiveRecord::Schema.define(:version => 20150225111538) do t.string "originator_type" t.boolean "eligible", :default => true t.string "adjustable_type" + t.decimal "included_tax", :precision => 10, :scale => 2 end add_index "spree_adjustments", ["adjustable_id"], :name => "index_adjustments_on_order_id" diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 60dfd631ce..dc17ac5eb2 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -4,5 +4,35 @@ module Spree adjustment = create(:adjustment, metadata: create(:adjustment_metadata)) adjustment.metadata.should be end + + describe "recording included tax" do + describe "TaxRate adjustments" do + let!(:zone) { create(:zone, default_tax: true) } + let!(:zone_member) { ZoneMember.create!(zone: zone, zoneable: Country.find_by_name('Australia')) } + let!(:order) { create(:order) } + let!(:line_item) { create(:line_item, order: order) } + let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::FlatRate.new(preferred_amount: 0.1)) } + let(:adjustment) { line_item.adjustments(:reload).first } + + before do + order.reload + tax_rate.adjust(order) + end + + it "has 100% tax included" do + adjustment.amount.should be > 0 + adjustment.included_tax.should == adjustment.amount + end + end + + describe "setting the included tax by fraction" do + let(:adjustment) { Adjustment.new label: 'foo', amount: 123.45 } + + it "sets it, rounding to two decimal places" do + adjustment.set_included_tax! 0.1 + adjustment.included_tax.should == 12.35 + end + end + end end end From dfb855bd14a40e0df8bb5cdb7c8f94445d0ccfc6 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 2 Mar 2015 15:17:14 +1100 Subject: [PATCH 157/384] Record the tax included in shipping. --- app/models/spree/shipment_decorator.rb | 11 ++++ ...5232938_add_included_tax_to_adjustments.rb | 2 +- db/schema.rb | 2 +- spec/models/spree/adjustment_spec.rb | 60 +++++++++++++++++-- 4 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 app/models/spree/shipment_decorator.rb diff --git a/app/models/spree/shipment_decorator.rb b/app/models/spree/shipment_decorator.rb new file mode 100644 index 0000000000..ee9189efdd --- /dev/null +++ b/app/models/spree/shipment_decorator.rb @@ -0,0 +1,11 @@ +module Spree + Shipment.class_eval do + def ensure_correct_adjustment_with_included_tax + ensure_correct_adjustment_without_included_tax + + adjustment.set_included_tax! Config.shipping_tax_rate if Config.shipment_inc_vat + end + + alias_method_chain :ensure_correct_adjustment, :included_tax + end +end diff --git a/db/migrate/20150225232938_add_included_tax_to_adjustments.rb b/db/migrate/20150225232938_add_included_tax_to_adjustments.rb index fa28ab20c7..f3815dec2a 100644 --- a/db/migrate/20150225232938_add_included_tax_to_adjustments.rb +++ b/db/migrate/20150225232938_add_included_tax_to_adjustments.rb @@ -1,5 +1,5 @@ class AddIncludedTaxToAdjustments < ActiveRecord::Migration def change - add_column :spree_adjustments, :included_tax, :decimal, precision: 10, scale: 2 + add_column :spree_adjustments, :included_tax, :decimal, precision: 10, scale: 2, null: false, default: 0 end end diff --git a/db/schema.rb b/db/schema.rb index cc55ad2392..a325b69f80 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -416,7 +416,7 @@ ActiveRecord::Schema.define(:version => 20150225232938) do t.string "originator_type" t.boolean "eligible", :default => true t.string "adjustable_type" - t.decimal "included_tax", :precision => 10, :scale => 2 + t.decimal "included_tax", :precision => 10, :scale => 2, :default => 0.0, :null => false end add_index "spree_adjustments", ["adjustable_id"], :name => "index_adjustments_on_order_id" diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index dc17ac5eb2..44d3cab868 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -7,12 +7,12 @@ module Spree describe "recording included tax" do describe "TaxRate adjustments" do - let!(:zone) { create(:zone, default_tax: true) } + let!(:zone) { create(:zone, default_tax: true) } let!(:zone_member) { ZoneMember.create!(zone: zone, zoneable: Country.find_by_name('Australia')) } - let!(:order) { create(:order) } - let!(:line_item) { create(:line_item, order: order) } - let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::FlatRate.new(preferred_amount: 0.1)) } - let(:adjustment) { line_item.adjustments(:reload).first } + let!(:order) { create(:order) } + let!(:line_item) { create(:line_item, order: order) } + let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::FlatRate.new(preferred_amount: 0.1)) } + let(:adjustment) { line_item.adjustments(:reload).first } before do order.reload @@ -25,6 +25,56 @@ module Spree end end + describe "Shipment adjustments" do + let!(:order) { create(:order, shipping_method: shipping_method) } + let!(:line_item) { create(:line_item, order: order) } + let(:shipping_method) { create(:shipping_method, calculator: Calculator::FlatRate.new(preferred_amount: 50.0)) } + let(:adjustment) { order.adjustments(:reload).shipping.first } + + it "has a shipping charge of $50" do + order.create_shipment! + adjustment.amount.should == 50 + end + + context "when tax on shipping is disabled" do + it "records 0% tax on shipment adjustments" do + Config.shipment_inc_vat = false + Config.shipping_tax_rate = 0 + order.create_shipment! + + adjustment.included_tax.should == 0 + end + + it "records 0% tax on shipments when a rate is set but shipment_inc_vat is false" do + Config.shipment_inc_vat = false + Config.shipping_tax_rate = 0.25 + order.create_shipment! + + adjustment.included_tax.should == 0 + end + end + + context "when tax on shipping is enabled" do + before do + Config.shipment_inc_vat = true + Config.shipping_tax_rate = 0.25 + order.create_shipment! + end + + it "takes the shipment adjustment tax included from the system setting" do + adjustment.included_tax.should == 12.50 + end + + it "records 0% tax on shipments when shipping_tax_rate is not set" do + Config.shipment_inc_vat = true + Config.shipping_tax_rate = nil + order.create_shipment! + + adjustment.included_tax.should == 0 + end + end + end + describe "setting the included tax by fraction" do let(:adjustment) { Adjustment.new label: 'foo', amount: 123.45 } From 4e1eb33ff5a6a2a6174c563f15481ff7e7d98220 Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Thu, 5 Mar 2015 11:25:35 +1100 Subject: [PATCH 158/384] Redirect users to proper login page when they type /login --- config/routes.rb | 2 ++ spec/features/consumer/authentication_spec.rb | 29 ++++++------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index e7f9f2bba0..33a31023b8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,9 @@ Openfoodnetwork::Application.routes.draw do root :to => 'home#index' + get "/#/login", to: "home#index", as: :spree_login + get "/login", to: redirect("/#/login") get "/map", to: "map#index", as: :map diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb index f8b405374e..2aefb42db5 100644 --- a/spec/features/consumer/authentication_spec.rb +++ b/spec/features/consumer/authentication_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' feature "Authentication", js: true do include UIComponentHelper + describe "login" do let(:user) { create(:user, password: "password", password_confirmation: "password") } @@ -32,7 +33,7 @@ feature "Authentication", js: true do scenario "failing to login" do fill_in "Email", with: user.email click_login_button - page.should have_content "Invalid email or password" + page.should have_content "Invalid email or password" end scenario "logging in successfully" do @@ -70,7 +71,7 @@ feature "Authentication", js: true do ActionMailer::Base.deliveries.clear select_login_tab "Forgot Password?" end - + scenario "failing to reset password" do fill_in "Your email", with: "notanemail@myemail.com" click_reset_password_button @@ -78,7 +79,7 @@ feature "Authentication", js: true do end scenario "resetting password" do - fill_in "Your email", with: user.email + fill_in "Your email", with: user.email click_reset_password_button page.should have_reset_password ActionMailer::Base.deliveries.last.subject.should =~ /Password Reset/ @@ -90,29 +91,17 @@ feature "Authentication", js: true do browse_as_medium end scenario "showing login" do - open_off_canvas + open_off_canvas open_login_modal page.should have_login_modal end end end - describe "oldskool" do - scenario "with valid credentials" do - visit "/login" - fill_in "Email", with: user.email - fill_in "Password", with: "password" - click_button "Login" - current_path.should == "/" - end - - scenario "with invalid credentials" do - visit "/login" - fill_in "Email", with: user.email - fill_in "Password", with: "this isn't my password" - click_button "Login" - page.should have_content "Invalid email or password" - end + scenario "Loggin by typing login/ redirects to /#/login" do + visit "/login" + uri = URI.parse(current_url) + (uri.path + "#" + uri.fragment).should == '/#/login' end end end From 235bb009e8f042ae316ac002492f72b479819377 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Mar 2015 11:25:57 +1100 Subject: [PATCH 159/384] Tweaking styling for order confirmation page --- app/assets/stylesheets/darkswarm/checkout.css.sass | 1 - app/assets/stylesheets/darkswarm/typography.css.sass | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/darkswarm/checkout.css.sass b/app/assets/stylesheets/darkswarm/checkout.css.sass index 086678adb3..bcca5310f5 100644 --- a/app/assets/stylesheets/darkswarm/checkout.css.sass +++ b/app/assets/stylesheets/darkswarm/checkout.css.sass @@ -5,7 +5,6 @@ .order-summary background-color: #e1f0f5 padding: 1em - margin-bottom: 0.5em checkout display: block diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index a0236d9337..d75567dcb2 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -85,6 +85,9 @@ ul.check-list .light-grey color: #666666 +.pad + padding: 1em + .pad-top padding-top: 1em From 2cdab7af25da6d6f6816096009cc46549d2bf244 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Mar 2015 11:26:15 +1100 Subject: [PATCH 160/384] More label tweaks and logic tweaks to templates --- .../spree/order_mailer/confirm_email_for_customer.html.haml | 2 +- app/views/spree/order_mailer/confirm_email_for_shop.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index ba5fabf74f..c769615b0b 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -149,7 +149,7 @@ %br %p %small - %strong Your notes / custom delivery instructions: + %strong Your notes: %br #{@order.special_instructions} diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index bddeaa6478..a99c448c6f 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -148,7 +148,7 @@ %br %p %small - %strong Customer notes / custom delivery instructions: + %strong Customer notes: %br #{@order.special_instructions} From 574a2155259fadbbcb1c414e20c73db79f3cbb2e Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Mar 2015 11:26:29 +1100 Subject: [PATCH 161/384] Order Confirmation Page WIP --- .../spree/shared/_order_details.html.haml | 76 +++++++++++-------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index 8885ccd111..822d34f984 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -11,55 +11,69 @@ %strong = order.display_total.to_html / = link_to "(#{t(:edit)})", checkout_state_path(:payment) unless @order.completed? - %p.text-big - Paying via: - %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe - %br - %span.text-small - %em - = order.payments.first.andand.payment_method.andand.description.andand.html_safe + .pad + %p.text-big + Paying via: + %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe + %br + %span.text-small + %em + = order.payments.first.andand.payment_method.andand.description.andand.html_safe .order-summary.text-small %strong Billing address = link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? - %p.text-small - = order.bill_address.firstname + " " + order.bill_address.lastname - %br - = order.bill_address.full_address + .pad + %p.text-small + = order.bill_address.firstname + " " + order.bill_address.lastname + %br + = order.bill_address.full_address + %br + = order.bill_address.phone .columns.large-6 .order-summary.text-small %strong= order.shipping_method.name - if @order.has_step?("delivery") - %p.text-big - Delivery on - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - %br - %span.text-small - %em= order.shipping_method.description.html_safe + .pad + %p.text-big + Delivery on + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %br + %span.text-small + %em= order.shipping_method.description.html_safe .order-summary.text-small %strong Delivery address - %p.text-small - = order.ship_address.firstname + " " + order.ship_address.lastname - %br - = order.ship_address.full_address + .pad + %p.text-small + = order.ship_address.firstname + " " + order.ship_address.lastname + %br + = order.ship_address.full_address + %br + = order.ship_address.phone + + %p.light.small + %strong Your notes: + %br + = order.special_instructions - else .order-summary.text-small %strong Shipping address = link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? - %p.text-small - = order.ship_address.firstname + " " + order.ship_address.lastname - %br - = order.ship_address.full_address - - %p.light - %small - %strong Your notes / custom instructions: - %br - = order.special_instructions + .pad + %p.text-small + = order.ship_address.firstname + " " + order.ship_address.lastname + %br + = order.ship_address.full_address + %br + = order.ship_address.phone + %p.light.small + %strong Your notes: + %br + = order.special_instructions %br %table#line-items{"data-hook" => "order_details"} From b5ce056d0679d0d4cd83cc651c028f66c7ea706a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 5 Mar 2015 13:03:53 +1100 Subject: [PATCH 162/384] Fix tax calculations for determining tax included in an amount --- app/models/spree/adjustment_decorator.rb | 9 +++++++-- app/models/spree/tax_rate_decorator.rb | 2 +- spec/models/spree/adjustment_spec.rb | 14 +++++++++----- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/models/spree/adjustment_decorator.rb b/app/models/spree/adjustment_decorator.rb index b9b89cde6a..836080183c 100644 --- a/app/models/spree/adjustment_decorator.rb +++ b/app/models/spree/adjustment_decorator.rb @@ -7,8 +7,13 @@ module Spree attr_accessible :included_tax - def set_included_tax!(fraction) - update_attributes! included_tax: (amount * fraction).round(2) + def set_included_tax!(rate) + tax = amount - (amount / (1 + rate)) + set_absolute_included_tax! tax + end + + def set_absolute_included_tax!(tax) + update_attributes! included_tax: tax.round(2) end end end diff --git a/app/models/spree/tax_rate_decorator.rb b/app/models/spree/tax_rate_decorator.rb index f8a0333ce2..e41c20db70 100644 --- a/app/models/spree/tax_rate_decorator.rb +++ b/app/models/spree/tax_rate_decorator.rb @@ -3,7 +3,7 @@ Spree::TaxRate.class_eval do adjust_without_included_tax(order) (order.adjustments.tax + order.price_adjustments).each do |a| - a.set_included_tax! 1.0 + a.set_absolute_included_tax! a.amount end end diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 44d3cab868..acd0fa64ac 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -62,7 +62,11 @@ module Spree end it "takes the shipment adjustment tax included from the system setting" do - adjustment.included_tax.should == 12.50 + # Finding the tax included in an amount that's already inclusive of tax: + # total - ( total / (1 + rate) ) + # 50 - ( 50 / (1 + 0.25) ) + # = 10 + adjustment.included_tax.should == 10.00 end it "records 0% tax on shipments when shipping_tax_rate is not set" do @@ -75,12 +79,12 @@ module Spree end end - describe "setting the included tax by fraction" do - let(:adjustment) { Adjustment.new label: 'foo', amount: 123.45 } + describe "setting the included tax by tax rate" do + let(:adjustment) { Adjustment.new label: 'foo', amount: 50 } it "sets it, rounding to two decimal places" do - adjustment.set_included_tax! 0.1 - adjustment.included_tax.should == 12.35 + adjustment.set_included_tax! 0.25 + adjustment.included_tax.should == 10.00 end end end From 1b709a3e0370bb7db9ff8e6e3818d026fab65add Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Thu, 5 Mar 2015 16:03:48 +1100 Subject: [PATCH 163/384] Do not load Order Cycles that closed more than a month a go --- Gemfile.lock | 4 ++-- app/controllers/admin/order_cycles_controller.rb | 2 +- app/models/order_cycle.rb | 2 ++ db/schema.rb | 2 +- spec/models/order_cycle_spec.rb | 10 ++++++++++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 57e0877ad2..320e680184 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -325,7 +325,7 @@ GEM mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - method_source (0.8.1) + method_source (0.8.2) mime-types (1.25.1) mini_portile (0.6.2) momentjs-rails (2.5.1) @@ -509,7 +509,7 @@ GEM xml-simple (1.1.4) xpath (2.0.0) nokogiri (~> 1.3) - zeus (0.13.3) + zeus (0.15.4) method_source (>= 0.6.7) PLATFORMS diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 04a6d234c6..22a7aaa3cc 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -79,7 +79,7 @@ module Admin ocs.undated + ocs.soonest_closing + ocs.soonest_opening + - ocs.most_recently_closed + ocs.recently_closed end private diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 134af04c91..bf52d5c129 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -19,7 +19,9 @@ class OrderCycle < ActiveRecord::Base scope :undated, where(orders_open_at: nil, orders_close_at: nil) scope :soonest_closing, lambda { active.order('order_cycles.orders_close_at ASC') } + # TODO This method returns all the closed orders. So maybe we can replace it with :recently_closed. scope :most_recently_closed, lambda { closed.order('order_cycles.orders_close_at DESC') } + scope :recently_closed, lambda { where("order_cycles.orders_close_at >= ?", 31.days.ago).order("order_cycles.orders_close_at DESC") } scope :soonest_opening, lambda { upcoming.order('order_cycles.orders_open_at ASC') } scope :distributing_product, lambda { |product| diff --git a/db/schema.rb b/db/schema.rb index a8ac91e6d8..6ef4fe2ee9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -585,9 +585,9 @@ ActiveRecord::Schema.define(:version => 20150220035501) do t.string "email" t.text "special_instructions" t.integer "distributor_id" - t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" + t.integer "order_cycle_id" t.integer "cart_id" end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 7ba2b66589..cc6035caa8 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -90,6 +90,16 @@ describe OrderCycle do end end + describe "#recently_closed" do + it "finds the orders closed in the last 30 days sorted in descending order" do + oc1 = create(:simple_order_cycle, orders_close_at: 1.day.ago) + oc2 = create(:simple_order_cycle, orders_close_at: 30.days.ago) + create(:simple_order_cycle, orders_close_at: 31.days.ago) + + OrderCycle.recently_closed.should == [oc1 , oc2] + end + end + it "finds the most recently closed order cycles" do oc1 = create(:simple_order_cycle, orders_close_at: 2.hours.ago) oc2 = create(:simple_order_cycle, orders_close_at: 1.hour.ago) From a21bfc909aa24e0290fd2077a7bca7a6a2e5aef6 Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Thu, 5 Mar 2015 16:12:31 +1100 Subject: [PATCH 164/384] Remove the suppliers column on admin order cycles index page --- app/views/admin/order_cycles/index.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index 995cfdd2c3..afe18bc230 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -25,7 +25,6 @@ %th Open %th Close - unless order_cycles_simple_index - %th Suppliers %th Coordinator %th Distributors %th Products From 9a86e419420b9d04ceba5125871358cbd2378855 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Mar 2015 17:20:02 +1100 Subject: [PATCH 165/384] Adding more helper classes --- app/assets/stylesheets/darkswarm/typography.css.sass | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index d75567dcb2..fe0dbc8136 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -39,6 +39,14 @@ small, .small &, & * font-size: 0.875rem +.text-normal + font-weight: 400 + font-family: 'Open Sans' + +.text-skinny + font-weight: 300 + font-family: 'Open Sans' + .word-wrap word-wrap: break-word From e3b660abb9fbf314df56551a74530c86293a9d30 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Mar 2015 17:21:13 +1100 Subject: [PATCH 166/384] Shopping cart page update to simplify, improve and expose fees --- app/views/spree/orders/_adjustments.html.haml | 6 +- app/views/spree/orders/_form.html.haml | 94 +++++++++++-------- app/views/spree/orders/_line_item.html.haml | 31 ++++-- 3 files changed, 80 insertions(+), 51 deletions(-) diff --git a/app/views/spree/orders/_adjustments.html.haml b/app/views/spree/orders/_adjustments.html.haml index 71579e3ace..b5956ba2cc 100644 --- a/app/views/spree/orders/_adjustments.html.haml +++ b/app/views/spree/orders/_adjustments.html.haml @@ -1,11 +1,11 @@ %thead %tr{"data-hook" => "cart_adjustments_headers"} - %th.cart-adjustment-header{colspan: "6"} + %th.cart-adjustment-header{colspan: "5"} %a{ href: "#" } Fees... %tbody#cart_adjustments{"data-hook" => ""} - checkout_line_item_adjustments(@order).each do |adjustment| %tr - %td{colspan: "4"}= adjustment.label - %td= adjustment.display_amount.to_html + %td{colspan: "3"}= adjustment.label + %td.text-right= adjustment.display_amount.to_html %td diff --git a/app/views/spree/orders/_form.html.haml b/app/views/spree/orders/_form.html.haml index 435ff03ff6..5f7183a6cd 100644 --- a/app/views/spree/orders/_form.html.haml +++ b/app/views/spree/orders/_form.html.haml @@ -1,45 +1,59 @@ = render :partial => 'spree/shared/error_messages', :locals => { :target => @order } -%table#cart-detail{"data-hook" => ""} - %col{halign: "center", valign: "middle", width: "30%"}/ - %col{valign: "middle", width: "25%"}/ - %col{halign: "center", valign: "middle", width: "15%"}/ - %col{halign: "center", valign: "middle", width: "15%"}/ - %col{halign: "center", valign: "middle", width: "10%"}/ - %col{halign: "center", valign: "middle", width: "5%"}/ - %thead - %tr{"data-hook" => "cart_items_headers"} - %th.cart-item-description-header{colspan: "2"}= t(:item) - %th.cart-item-price-header= t(:price) - %th.cart-item-quantity-header= t(:qty) - %th.cart-item-total-header= t(:total) - %th.cart-item-delete-header +.row + .columns.large-12 + %table#cart-detail{"data-hook" => ""} + / %col{halign: "center", valign: "middle", width: "30%"}/ + %col{halign: "left", valign: "middle"}/ + %col{halign: "left", valign: "middle", width: "15%"}/ + %col{halign: "center", valign: "middle", width: "10%"}/ + %col{halign: "center", valign: "middle", width: "10%"}/ + %col{halign: "center", valign: "middle", width: "5%"}/ + %thead + %tr{"data-hook" => "cart_items_headers"} + %th.cart-item-description-header= t(:item) + %th.cart-item-price-header= t(:price) + %th.cart-item-quantity-header= t(:qty) + %th.cart-item-total-header.text-right= t(:total) + %th.cart-item-delete-header - %tbody#line_items{"data-hook" => ""} - = order_form.fields_for :line_items do |item_form| - = render :partial => 'line_item', :locals => { :variant => item_form.object.variant, :line_item => item_form.object, :item_form => item_form } + %tbody#line_items{"data-hook" => ""} + = order_form.fields_for :line_items do |item_form| + = render :partial => 'line_item', :locals => { :variant => item_form.object.variant, :line_item => item_form.object, :item_form => item_form } + + %tfoot#edit-cart + %tr + %td{colspan:"2"} + %td + = button_tag :class => 'secondary radius expand small', :id => 'update-button' do + %i.ofn-i_023-refresh + = t(:update) + %td + %td#empty-cart.text-center + %span#clear_cart_link{"data-hook" => ""} + = link_to "Empty cart", empty_cart_path, method: :put, :class => 'not-bold small' + -#= form_tag empty_cart_path, :method => :put do + -#= submit_tag t(:empty_cart), :class => 'button alert expand small' + %tr + %td.text-right{colspan:"3"} Produce subtotal + %td.text-right + %span.order-total.item-total= display_checkout_subtotal(@order) + %td - %tfoot#edit-cart - %tr - %td - Produce Subtotal - \: - %span.order-total.item-total= display_checkout_subtotal(@order) - %td - Admin & Handling - \: - %span.order-total.distribution-total= display_checkout_admin_and_handling_adjustments_total_for(@order) - %td - %td - = button_tag :class => 'secondary radius expand small', :id => 'update-button' do - %i.ofn-i_023-refresh - = t(:update) - %td - %h5.order-total.grand-total= @order.display_total - %td#empty-cart.text-center - %span#clear_cart_link{"data-hook" => ""} - = link_to "Empty cart", empty_cart_path, method: :put, :class => 'not-bold small' - -#= form_tag empty_cart_path, :method => :put do - -#= submit_tag t(:empty_cart), :class => 'button alert expand small' + %tr + %td.text-right{colspan:"3"} Admin & handling + %td.text-right + %span.order-total.distribution-total= display_checkout_admin_and_handling_adjustments_total_for(@order) + %td + + / %tr + / %td{colspan:"5"} + / = render "spree/orders/adjustments" unless @order.adjustments.eligible.blank? + + %tr + %td.text-right{colspan:"3"} + %h5 Total + %td.text-right + %h5.order-total.grand-total= @order.display_total + %td - = render "spree/orders/adjustments" unless @order.adjustments.eligible.blank? diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index 7c11e40bb3..23af2320b8 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -1,13 +1,23 @@ %tr.line-item{class: "variant-#{variant.id}"} - %td.cart-item-image{"data-hook" => "cart_item_image"} - - if variant.images.length == 0 - = link_to small_image(variant.product), variant.product - - else - = link_to image_tag(variant.images.first.attachment.url(:small)), variant.product + + / %td.cart-item-image{"data-hook" => "cart_item_image"} + / - if variant.images.length == 0 + / = link_to small_image(variant.product), variant.product + / - else + / = link_to image_tag(variant.images.first.attachment.url(:small)), variant.product %td.cart-item-description{'data-hook' => "cart_item_description"} - %h4= variant.product.name - = variant.options_text + / %h4= variant.product.name + - if variant.product.name == variant.name_to_display + %h5 + %span= variant.product.name + %span.text-small.text-skinny= " (" + variant.options_text + ")" unless variant.options_text.empty? + - else + %h5 + %span= variant.product.name + %span= "- " + variant.name_to_display + %span.text-small.text-skinny= " (" + variant.options_text + ")" unless variant.options_text.empty? + - if @order.insufficient_stock_lines.include? line_item %span.out-of-stock = variant.in_stock? ? t(:insufficient_stock, :on_hand => variant.on_hand) : t(:out_of_stock) @@ -15,9 +25,14 @@ %td.cart-item-price{"data-hook" => "cart_item_price"} = line_item.single_display_amount_with_adjustments.to_html + -# Now in a template in app/assets/javascripts/templates ! + %price-breakdown{"price-breakdown" => "_", variant: "variant", + "price-breakdown-append-to-body" => "true", + "price-breakdown-placement" => "left", + "price-breakdown-animation" => true} %td.cart-item-quantity{"data-hook" => "cart_item_quantity"} = item_form.number_field :quantity, :min => 0, :class => "line_item_quantity", :size => 5 - %td.cart-item-total{"data-hook" => "cart_item_total"} + %td.cart-item-total.text-right{"data-hook" => "cart_item_total"} = line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil? %td.cart-item-delete.text-center{"data-hook" => "cart_item_delete"} From a0af22350dbf09687447ebc8a44bfad838065d76 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Mar 2015 17:21:25 +1100 Subject: [PATCH 167/384] Order conf page WIP --- .../spree/shared/_order_details.html.haml | 73 +++++++++---------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index 822d34f984..c66f8d9a92 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -10,7 +10,6 @@ Total order %strong = order.display_total.to_html - / = link_to "(#{t(:edit)})", checkout_state_path(:payment) unless @order.completed? .pad %p.text-big Paying via: @@ -22,7 +21,6 @@ .order-summary.text-small %strong Billing address - = link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? .pad %p.text-small = order.bill_address.firstname + " " + order.bill_address.lastname @@ -34,46 +32,45 @@ .columns.large-6 .order-summary.text-small %strong= order.shipping_method.name - - - if @order.has_step?("delivery") - .pad - %p.text-big + + .pad + %p.text-big + - if @order.shipping_method.andand.require_ship_address Delivery on - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - %br - %span.text-small - %em= order.shipping_method.description.html_safe - .order-summary.text-small - %strong Delivery address - .pad - %p.text-small + - else + Ready for collection + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %br + %span.text-small + %em= order.shipping_method.description.html_safe + .order-summary.text-small + %strong + - if @order.shipping_method.andand.require_ship_address + Delivery address + - else + Collection Address + .pad + %p.text-small + - if @order.shipping_method.andand.require_ship_address = order.ship_address.firstname + " " + order.ship_address.lastname %br - = order.ship_address.full_address - %br - = order.ship_address.phone - - %p.light.small - %strong Your notes: - %br - = order.special_instructions + - else + / Do nothing + + = order.ship_address.full_address + %br + = order.ship_address.phone + + - if @order.shipping_method.andand.require_ship_address + / Do nothing + - else + / Collection instructions + + %p.light.small + %strong Your notes: + %br + = order.special_instructions - - else - .order-summary.text-small - %strong - Shipping address - = link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? - .pad - %p.text-small - = order.ship_address.firstname + " " + order.ship_address.lastname - %br - = order.ship_address.full_address - %br - = order.ship_address.phone - %p.light.small - %strong Your notes: - %br - = order.special_instructions %br %table#line-items{"data-hook" => "order_details"} From d6c30ae1ef8b7c2cfa201a71c3e5c6f70908d46b Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Thu, 5 Mar 2015 16:46:51 +1100 Subject: [PATCH 168/384] OrderCycle#recently_closed doesn't return orders that are open --- app/models/order_cycle.rb | 7 ++++++- spec/models/order_cycle_spec.rb | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index bf52d5c129..adcd596a8c 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -21,7 +21,12 @@ class OrderCycle < ActiveRecord::Base scope :soonest_closing, lambda { active.order('order_cycles.orders_close_at ASC') } # TODO This method returns all the closed orders. So maybe we can replace it with :recently_closed. scope :most_recently_closed, lambda { closed.order('order_cycles.orders_close_at DESC') } - scope :recently_closed, lambda { where("order_cycles.orders_close_at >= ?", 31.days.ago).order("order_cycles.orders_close_at DESC") } + + scope :recently_closed, -> { + closed. + where("order_cycles.orders_close_at >= ?", 31.days.ago). + order("order_cycles.orders_close_at DESC") } + scope :soonest_opening, lambda { upcoming.order('order_cycles.orders_open_at ASC') } scope :distributing_product, lambda { |product| diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index cc6035caa8..817d2e8e93 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -92,6 +92,7 @@ describe OrderCycle do describe "#recently_closed" do it "finds the orders closed in the last 30 days sorted in descending order" do + create(:simple_order_cycle, orders_close_at: 3.days.from_now) oc1 = create(:simple_order_cycle, orders_close_at: 1.day.ago) oc2 = create(:simple_order_cycle, orders_close_at: 30.days.ago) create(:simple_order_cycle, orders_close_at: 31.days.ago) From 3fe1fc3f67c2df1a9975c622c03375d80f5c8ff7 Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Thu, 5 Mar 2015 17:32:55 +1100 Subject: [PATCH 169/384] Use dates way into the future so that test that depend on OrderSycles#recently_closed don't break. --- spec/features/admin/order_cycles_spec.rb | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index c84a6d65c7..2597417b75 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -83,8 +83,8 @@ feature %q{ # And I fill in the basic fields fill_in 'order_cycle_name', with: 'Plums & Avos' - fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00' - fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00' + fill_in 'order_cycle_orders_open_at', with: '2040-11-06 06:00:00' + fill_in 'order_cycle_orders_close_at', with: '2040-11-13 17:00:00' # And I add a coordinator fee click_button 'Add coordinator fee' @@ -126,8 +126,8 @@ feature %q{ page.should have_selector 'a', text: 'Plums & Avos' - page.should have_selector "input[value='2012-11-06 06:00:00 +1100']" - page.should have_selector "input[value='2012-11-13 17:00:00 +1100']" + page.should have_selector "input[value='2040-11-06 06:00:00 +1100']" + page.should have_selector "input[value='2040-11-13 17:00:00 +1100']" page.should have_content 'My coordinator' page.should have_selector 'td.suppliers', text: 'My supplier' @@ -271,8 +271,8 @@ feature %q{ # And I update it fill_in 'order_cycle_name', with: 'Plums & Avos' - fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00' - fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00' + fill_in 'order_cycle_orders_open_at', with: '2040-11-06 06:00:00' + fill_in 'order_cycle_orders_close_at', with: '2040-11-13 17:00:00' select 'My coordinator', from: 'order_cycle_coordinator_id' # And I configure some coordinator fees @@ -335,8 +335,8 @@ feature %q{ page.should have_selector 'a', text: 'Plums & Avos' - page.should have_selector "input[value='2012-11-06 06:00:00 +1100']" - page.should have_selector "input[value='2012-11-13 17:00:00 +1100']" + page.should have_selector "input[value='2040-11-06 06:00:00 +1100']" + page.should have_selector "input[value='2040-11-13 17:00:00 +1100']" page.should have_content 'My coordinator' page.should have_selector 'td.suppliers', text: 'My supplier' @@ -373,18 +373,18 @@ feature %q{ # And I fill in some new opening/closing times and save them within("tr.order-cycle-#{oc1.id}") do - all('input').first.set '2012-12-01 12:00:00' - all('input').last.set '2012-12-01 12:00:01' + all('input').first.set '2040-12-01 12:00:00' + all('input').last.set '2040-12-01 12:00:01' end within("tr.order-cycle-#{oc2.id}") do - all('input').first.set '2012-12-01 12:00:02' - all('input').last.set '2012-12-01 12:00:03' + all('input').first.set '2040-12-01 12:00:02' + all('input').last.set '2040-12-01 12:00:03' end within("tr.order-cycle-#{oc3.id}") do - all('input').first.set '2012-12-01 12:00:04' - all('input').last.set '2012-12-01 12:00:05' + all('input').first.set '2040-12-01 12:00:04' + all('input').last.set '2040-12-01 12:00:05' end click_button 'Update' @@ -528,8 +528,8 @@ feature %q{ save_screenshot '/Users/rob/Desktop/ss1.png' fill_in 'order_cycle_name', with: 'My order cycle' - fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00' - fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00' + fill_in 'order_cycle_orders_open_at', with: '2040-11-06 06:00:00' + fill_in 'order_cycle_orders_close_at', with: '2040-11-13 17:00:00' select 'Managed supplier', from: 'new_supplier_id' click_button 'Add supplier' @@ -650,8 +650,8 @@ feature %q{ # And I fill in the basic fields fill_in 'order_cycle_name', with: 'Plums & Avos' - fill_in 'order_cycle_orders_open_at', with: '2014-10-17 06:00:00' - fill_in 'order_cycle_orders_close_at', with: '2014-10-24 17:00:00' + fill_in 'order_cycle_orders_open_at', with: '2040-10-17 06:00:00' + fill_in 'order_cycle_orders_close_at', with: '2040-10-24 17:00:00' fill_in 'order_cycle_outgoing_exchange_0_pickup_time', with: 'pickup time' fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'pickup instructions' @@ -676,8 +676,8 @@ feature %q{ # Then my order cycle should have been created page.should have_content 'Your order cycle has been created.' page.should have_selector 'a', text: 'Plums & Avos' - page.should have_selector "input[value='2014-10-17 06:00:00 +1100']" - page.should have_selector "input[value='2014-10-24 17:00:00 +1100']" + page.should have_selector "input[value='2040-10-17 06:00:00 +1100']" + page.should have_selector "input[value='2040-10-24 17:00:00 +1100']" # And it should have some variants selected oc = OrderCycle.last @@ -737,8 +737,8 @@ feature %q{ # And I fill in the basic fields fill_in 'order_cycle_name', with: 'Plums & Avos' - fill_in 'order_cycle_orders_open_at', with: '2014-10-17 06:00:00' - fill_in 'order_cycle_orders_close_at', with: '2014-10-24 17:00:00' + fill_in 'order_cycle_orders_open_at', with: '2040-10-17 06:00:00' + fill_in 'order_cycle_orders_close_at', with: '2040-10-24 17:00:00' fill_in 'order_cycle_outgoing_exchange_0_pickup_time', with: 'xy' fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'zzy' @@ -759,8 +759,8 @@ feature %q{ # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' page.should have_selector 'a', text: 'Plums & Avos' - page.should have_selector "input[value='2014-10-17 06:00:00 +1100']" - page.should have_selector "input[value='2014-10-24 17:00:00 +1100']" + page.should have_selector "input[value='2040-10-17 06:00:00 +1100']" + page.should have_selector "input[value='2040-10-24 17:00:00 +1100']" # And it should have a variant selected oc = OrderCycle.last From c3c1573f54544308b0b5bcf307ec3ca027b7d225 Mon Sep 17 00:00:00 2001 From: summerscope Date: Thu, 5 Mar 2015 23:22:31 +1100 Subject: [PATCH 170/384] Adding a comment --- app/views/spree/orders/_form.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/spree/orders/_form.html.haml b/app/views/spree/orders/_form.html.haml index 5f7183a6cd..f0edc75221 100644 --- a/app/views/spree/orders/_form.html.haml +++ b/app/views/spree/orders/_form.html.haml @@ -45,7 +45,8 @@ %td.text-right %span.order-total.distribution-total= display_checkout_admin_and_handling_adjustments_total_for(@order) %td - + + / This is the fees row which we want to replace with the pop-over / %tr / %td{colspan:"5"} / = render "spree/orders/adjustments" unless @order.adjustments.eligible.blank? From 98ff895f5fcb7bfbd28463a70846698da6c1a07a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 6 Mar 2015 10:35:40 +1100 Subject: [PATCH 171/384] Record the tax included in per-order EnterpriseFees --- .../enterprise_fee_applicator.rb | 44 ++++++++++++++++ .../enterprise_fee_applicator_spec.rb | 29 +++++++++++ spec/models/spree/adjustment_spec.rb | 51 +++++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/lib/open_food_network/enterprise_fee_applicator.rb b/lib/open_food_network/enterprise_fee_applicator.rb index 45905bfaca..9a2bd657b5 100644 --- a/lib/open_food_network/enterprise_fee_applicator.rb +++ b/lib/open_food_network/enterprise_fee_applicator.rb @@ -2,12 +2,16 @@ module OpenFoodNetwork class EnterpriseFeeApplicator < Struct.new(:enterprise_fee, :variant, :role) def create_line_item_adjustment(line_item) a = enterprise_fee.create_locked_adjustment(line_item_adjustment_label, line_item.order, line_item, true) + AdjustmentMetadata.create! adjustment: a, enterprise: enterprise_fee.enterprise, fee_name: enterprise_fee.name, fee_type: enterprise_fee.fee_type, enterprise_role: role end def create_order_adjustment(order) a = enterprise_fee.create_locked_adjustment(order_adjustment_label, order, order, true) + AdjustmentMetadata.create! adjustment: a, enterprise: enterprise_fee.enterprise, fee_name: enterprise_fee.name, fee_type: enterprise_fee.fee_type, enterprise_role: role + + a.set_absolute_included_tax! adjustment_tax(order, a) end @@ -24,6 +28,46 @@ module OpenFoodNetwork def base_adjustment_label "#{enterprise_fee.fee_type} fee by #{role} #{enterprise_fee.enterprise.name}" end + + def adjustment_tax(order, adjustment) + enterprise_fee.tax_category.tax_rates.match(order).sum do |rate| + compute_tax rate, adjustment.amount + end + end + + # Apply a TaxRate to a particular amount. TaxRates normally compute against + # LineItems or Orders, so we mock out a line item here to fit the interface + # that our calculator (usually DefaultTax) expects. + def compute_tax(tax_rate, amount) + product = OpenStruct.new tax_category: tax_rate.tax_category + line_item = Spree::LineItem.new quantity: 1 + line_item.define_singleton_method(:product) { product } + line_item.define_singleton_method(:price) { amount } + + # The enterprise fee adjustments for which we're calculating tax are always inclusive of + # tax. However, there's nothing to stop an admin from setting one up with a tax rate + # that's marked as not inclusive of tax, and that would result in the DefaultTax + # calculator generating a slightly incorrect value. Therefore, we treat the tax + # rate as inclusive of tax for the calculations below, regardless of its original + # setting. + with_tax_included_in_price(tax_rate) do + tax_rate.calculator.compute line_item + end + end + + def with_tax_included_in_price(tax_rate) + old_included_in_price = tax_rate.included_in_price + + tax_rate.included_in_price = true + tax_rate.calculator.calculable.included_in_price = true + + result = yield + + tax_rate.included_in_price = old_included_in_price + tax_rate.calculator.calculable.included_in_price = old_included_in_price + + result + end end end diff --git a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb index 9ad1d222b3..6703f844ab 100644 --- a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb +++ b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb @@ -65,4 +65,33 @@ module OpenFoodNetwork efa.send(:order_adjustment_label).should == "Whole order - packing fee by distributor Ballantyne" end end + + describe "ensuring that tax rate is marked as tax included_in_price" do + let(:efa) { EnterpriseFeeApplicator.new nil, nil, nil } + let(:tax_rate) { create(:tax_rate, included_in_price: false, calculator: Spree::Calculator::DefaultTax.new) } + + it "sets included_in_price to true" do + efa.send(:with_tax_included_in_price, tax_rate) do + tax_rate.included_in_price.should be_true + end + end + + it "sets the included_in_price value accessible to the calculator to true" do + efa.send(:with_tax_included_in_price, tax_rate) do + tax_rate.calculator.calculable.included_in_price.should be_true + end + end + + it "passes through the return value of the block" do + efa.send(:with_tax_included_in_price, tax_rate) do + 'asdf' + end.should == 'asdf' + end + + it "restores both values to their original afterwards" do + efa.send(:with_tax_included_in_price, tax_rate) {} + tax_rate.included_in_price.should be_false + tax_rate.calculator.calculable.included_in_price.should be_false + end + end end diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index acd0fa64ac..d329db9546 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -79,6 +79,57 @@ module Spree end end + describe "EnterpriseFee adjustments" do + let!(:zone) { create(:zone, default_tax: true) } + let!(:zone_member) { ZoneMember.create!(zone: zone, zoneable: Country.find_by_name('Australia')) } + let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::DefaultTax.new, zone: zone, amount: 0.1) } + let(:tax_category) { create(:tax_category, tax_rates: [tax_rate]) } + let(:tax_category_untaxed) { create(:tax_category) } + + let(:coordinator) { create(:distributor_enterprise) } + let(:enterprise_fee) { create(:enterprise_fee, enterprise: coordinator, tax_category: tax_category, calculator: Calculator::FlatRate.new(preferred_amount: 50.0)) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: [enterprise_fee], distributors: [coordinator]) } + let!(:order) { create(:order, order_cycle: order_cycle, distributor: coordinator) } + let!(:line_item) { create(:line_item, order: order) } + let(:adjustment) { order.adjustments(:reload).enterprise_fee.first } + + before do + order.update_distribution_charge! + end + + context "when enterprise fees are taxed" do + it "records the tax on the enterprise fee adjustments" do + # The fee is $50, tax is 10%, and the fee is inclusive of tax + # Therefore, the included tax should be 0.1/1.1 * 50 = $4.55 + + adjustment.included_tax.should == 4.55 + end + + context "when the tax rate does not include the tax in the price" do + before do + tax_rate.update_attribute :included_in_price, false + order.update_distribution_charge! + end + + it "treats it as inclusive anyway" do + adjustment.included_tax.should == 4.55 + end + end + end + + context "when enterprise fees have no tax" do + before do + enterprise_fee.tax_category = tax_category_untaxed + enterprise_fee.save! + order.update_distribution_charge! + end + + it "records no tax as charged" do + adjustment.included_tax.should == 0 + end + end + end + describe "setting the included tax by tax rate" do let(:adjustment) { Adjustment.new label: 'foo', amount: 50 } From 41792395aaa96a6fa7d566229b384c47c71f3467 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 6 Mar 2015 11:13:47 +1100 Subject: [PATCH 172/384] Record the tax included in per-item EnterpriseFees --- .../enterprise_fee_applicator.rb | 2 + spec/models/spree/adjustment_spec.rb | 51 +++++++++++-------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/lib/open_food_network/enterprise_fee_applicator.rb b/lib/open_food_network/enterprise_fee_applicator.rb index 9a2bd657b5..f59c94f119 100644 --- a/lib/open_food_network/enterprise_fee_applicator.rb +++ b/lib/open_food_network/enterprise_fee_applicator.rb @@ -4,6 +4,8 @@ module OpenFoodNetwork a = enterprise_fee.create_locked_adjustment(line_item_adjustment_label, line_item.order, line_item, true) AdjustmentMetadata.create! adjustment: a, enterprise: enterprise_fee.enterprise, fee_name: enterprise_fee.name, fee_type: enterprise_fee.fee_type, enterprise_role: role + + a.set_absolute_included_tax! adjustment_tax(line_item.order, a) end def create_order_adjustment(order) diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index d329db9546..289b70ccab 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -80,24 +80,26 @@ module Spree end describe "EnterpriseFee adjustments" do - let!(:zone) { create(:zone, default_tax: true) } - let!(:zone_member) { ZoneMember.create!(zone: zone, zoneable: Country.find_by_name('Australia')) } - let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::DefaultTax.new, zone: zone, amount: 0.1) } - let(:tax_category) { create(:tax_category, tax_rates: [tax_rate]) } + let!(:zone) { create(:zone, default_tax: true) } + let!(:zone_member) { ZoneMember.create!(zone: zone, zoneable: Country.find_by_name('Australia')) } + let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::DefaultTax.new, zone: zone, amount: 0.1) } + let(:tax_category) { create(:tax_category, tax_rates: [tax_rate]) } let(:tax_category_untaxed) { create(:tax_category) } - let(:coordinator) { create(:distributor_enterprise) } - let(:enterprise_fee) { create(:enterprise_fee, enterprise: coordinator, tax_category: tax_category, calculator: Calculator::FlatRate.new(preferred_amount: 50.0)) } - let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: [enterprise_fee], distributors: [coordinator]) } - let!(:order) { create(:order, order_cycle: order_cycle, distributor: coordinator) } - let!(:line_item) { create(:line_item, order: order) } - let(:adjustment) { order.adjustments(:reload).enterprise_fee.first } + let(:coordinator) { create(:distributor_enterprise) } + let(:variant) { create(:variant) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: [enterprise_fee], distributors: [coordinator], variants: [variant]) } + let!(:order) { create(:order, order_cycle: order_cycle, distributor: coordinator) } + let!(:line_item) { create(:line_item, order: order, variant: variant) } + let(:adjustment) { order.adjustments(:reload).enterprise_fee.first } before do - order.update_distribution_charge! + order.reload.update_distribution_charge! end - context "when enterprise fees are taxed" do + context "when enterprise fees are taxed per-order" do + let(:enterprise_fee) { create(:enterprise_fee, enterprise: coordinator, tax_category: tax_category, calculator: Calculator::FlatRate.new(preferred_amount: 50.0)) } + it "records the tax on the enterprise fee adjustments" do # The fee is $50, tax is 10%, and the fee is inclusive of tax # Therefore, the included tax should be 0.1/1.1 * 50 = $4.55 @@ -115,17 +117,26 @@ module Spree adjustment.included_tax.should == 4.55 end end + + context "when enterprise fees have no tax" do + before do + enterprise_fee.tax_category = tax_category_untaxed + enterprise_fee.save! + order.update_distribution_charge! + end + + it "records no tax as charged" do + adjustment.included_tax.should == 0 + end + end end - context "when enterprise fees have no tax" do - before do - enterprise_fee.tax_category = tax_category_untaxed - enterprise_fee.save! - order.update_distribution_charge! - end - it "records no tax as charged" do - adjustment.included_tax.should == 0 + context "when enterprise fees are taxed per-item" do + let(:enterprise_fee) { create(:enterprise_fee, enterprise: coordinator, tax_category: tax_category, calculator: Calculator::PerItem.new(preferred_amount: 50.0)) } + + it "records the tax on the enterprise fee adjustments" do + adjustment.included_tax.should == 4.55 end end end From 1e18f773f57b2d70eaf05ada746c11a0a6337df9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 6 Mar 2015 11:15:07 +1100 Subject: [PATCH 173/384] Switch context -> describe, clarify test grammar --- spec/models/spree/adjustment_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 289b70ccab..0922597c2a 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -36,7 +36,7 @@ module Spree adjustment.amount.should == 50 end - context "when tax on shipping is disabled" do + describe "when tax on shipping is disabled" do it "records 0% tax on shipment adjustments" do Config.shipment_inc_vat = false Config.shipping_tax_rate = 0 @@ -54,7 +54,7 @@ module Spree end end - context "when tax on shipping is enabled" do + describe "when tax on shipping is enabled" do before do Config.shipment_inc_vat = true Config.shipping_tax_rate = 0.25 @@ -107,7 +107,7 @@ module Spree adjustment.included_tax.should == 4.55 end - context "when the tax rate does not include the tax in the price" do + describe "when the tax rate does not include the tax in the price" do before do tax_rate.update_attribute :included_in_price, false order.update_distribution_charge! @@ -118,7 +118,7 @@ module Spree end end - context "when enterprise fees have no tax" do + describe "when enterprise fees have no tax" do before do enterprise_fee.tax_category = tax_category_untaxed enterprise_fee.save! From 3e2142c3cf56441d595d60a7282405895c7ba822 Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Fri, 6 Mar 2015 11:33:03 +1100 Subject: [PATCH 174/384] Remove the hover / pop-up over the # variants on admin order cycles index page --- app/views/admin/order_cycles/_row.html.haml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index de18679cf6..6f1b389fd8 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -17,11 +17,7 @@ %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" + %span= "#{order_cycle.variants.count} variants" %td.actions = link_to '', main_app.edit_admin_order_cycle_path(order_cycle), class: 'edit-order-cycle icon-edit no-text' From 266d6ef834d902ae14355ff8d12cbfe90fe6e49b Mon Sep 17 00:00:00 2001 From: Liv Galendez Date: Fri, 6 Mar 2015 11:55:35 +1100 Subject: [PATCH 175/384] Fixed handling of distributors field --- app/views/admin/order_cycles/_row.html.haml | 22 +++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 91191a2a8a..a80cb7948a 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -7,13 +7,27 @@ - unless order_cycles_simple_index %td.suppliers - - order_cycle_permitted_in(order_cycle.suppliers).each do |s| - = s.name + - suppliers = order_cycle_permitted_in(order_cycle.suppliers) + - if suppliers.count > 3 + - supplier_list = suppliers.map(&:name).sort.join ', ' + %span.with-tip{'data-powertip' => supplier_list} + = suppliers.count + suppliers + - else + - suppliers.each do |s| + = s.name %br/ %td= order_cycle.coordinator.name %td.distributors - - order_cycle_permitted_in(order_cycle.distributors).each do |d| - = d.name + - distributors = order_cycle_permitted_in(order_cycle.distributors) + - if distributors.count > 3 + - distributor_list = distributors.map(&:name).sort.join ', ' + %span.with-tip{'data-powertip' => distributor_list} + = distributors.count + distributors + - else + - distributors.each do |d| + = d.name %br/ %td.products From 381bfd383bc29f5c1eddc04d501923dc10cfd868 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 6 Mar 2015 12:12:33 +1100 Subject: [PATCH 176/384] Allow enterprise fee with no tax category --- lib/open_food_network/enterprise_fee_applicator.rb | 4 +++- spec/models/spree/adjustment_spec.rb | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/open_food_network/enterprise_fee_applicator.rb b/lib/open_food_network/enterprise_fee_applicator.rb index f59c94f119..070cb4a91c 100644 --- a/lib/open_food_network/enterprise_fee_applicator.rb +++ b/lib/open_food_network/enterprise_fee_applicator.rb @@ -32,7 +32,9 @@ module OpenFoodNetwork end def adjustment_tax(order, adjustment) - enterprise_fee.tax_category.tax_rates.match(order).sum do |rate| + tax_rates = enterprise_fee.tax_category ? enterprise_fee.tax_category.tax_rates.match(order) : [] + + tax_rates.sum do |rate| compute_tax rate, adjustment.amount end end diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 0922597c2a..35746b28fd 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -84,7 +84,6 @@ module Spree let!(:zone_member) { ZoneMember.create!(zone: zone, zoneable: Country.find_by_name('Australia')) } let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::DefaultTax.new, zone: zone, amount: 0.1) } let(:tax_category) { create(:tax_category, tax_rates: [tax_rate]) } - let(:tax_category_untaxed) { create(:tax_category) } let(:coordinator) { create(:distributor_enterprise) } let(:variant) { create(:variant) } @@ -120,7 +119,7 @@ module Spree describe "when enterprise fees have no tax" do before do - enterprise_fee.tax_category = tax_category_untaxed + enterprise_fee.tax_category = nil enterprise_fee.save! order.update_distribution_charge! end From 881313edf9685e4f7dca9f31aa1ee6bb7553a099 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 6 Mar 2015 16:05:42 +1100 Subject: [PATCH 177/384] Changing the logic on Shipping accordion on checkout to try to show shipping price against option. Not working. --- app/views/checkout/_shipping.html.haml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/views/checkout/_shipping.html.haml b/app/views/checkout/_shipping.html.haml index ffb1e43de4..d06da7931e 100644 --- a/app/views/checkout/_shipping.html.haml +++ b/app/views/checkout/_shipping.html.haml @@ -21,6 +21,10 @@ "ng-value" => "method.id", "ng-model" => "order.shipping_method_id"} {{ method.name }} + // TODO: Laura - this is not working correctly, need to show shipping price regardless of if selected!? + %em.light{"ng-show" => "!Checkout.shippingPrice()"} (Free) + %em.light{"ng-show" => "order.shipping_method.andand.require_ship_address"} + {{ Checkout.shippingPrice() | localizeCurrency }} %small.error.medium.input-text{"ng-show" => "!fieldValid('order.shipping_method_id')"} = "{{ fieldErrors('order.shipping_method_id') }}" From de6abc34642258f2daee4a1cadf3cb86bb6f2f1f Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 11:34:31 +1100 Subject: [PATCH 178/384] Turning off z-index this causes problems on shopping cart page. Needs testing to confirm does not break elsewhere. --- app/assets/stylesheets/darkswarm/_shop-popovers.css.sass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass b/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass index b50586ab6e..38b77dd756 100644 --- a/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass @@ -75,7 +75,7 @@ ordercycle button.graph-button - z-index: 9999999 + // z-index: 9999999 border: 1px solid transparent padding: 0 margin: 0 From b9a057939716206cd731776a0016b6bf8c4dfa2f Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 11:35:03 +1100 Subject: [PATCH 179/384] Add styling for image thumbnail - hide for small, show small thumbnail for big screens. --- .../stylesheets/darkswarm/shopping-cart.css.sass | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/assets/stylesheets/darkswarm/shopping-cart.css.sass b/app/assets/stylesheets/darkswarm/shopping-cart.css.sass index 72856eba72..6dbbe103e6 100644 --- a/app/assets/stylesheets/darkswarm/shopping-cart.css.sass +++ b/app/assets/stylesheets/darkswarm/shopping-cart.css.sass @@ -36,10 +36,22 @@ li.product-cart border-top: 1px solid #424242 +// Shopping cart #cart-detail .cart-item-delete a.delete font-size: 1.125em + +.item-thumb-image + display: none + @media screen and (min-width: 640px) + display: inline-block + float: left + padding-right: 0.5em + width: 36px + height: 36px + + #edit-cart button, .button From c7cf9695bc327728bece0cf6d9961b53cc5b3c4e Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 11:35:35 +1100 Subject: [PATCH 180/384] WIP on cart menu trying to add the master name back in where a product has variante --- app/views/shared/menu/_cart.html.haml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/views/shared/menu/_cart.html.haml b/app/views/shared/menu/_cart.html.haml index 646bd33425..ab733cdab5 100644 --- a/app/views/shared/menu/_cart.html.haml +++ b/app/views/shared/menu/_cart.html.haml @@ -16,8 +16,16 @@ .row .columns.small-7 %small - %strong {{ line_item.variant.name_to_display }} - %em {{ line_item.variant.unit_to_display }} + / %strong {{ line_item.variant.name_to_display }} + / %em {{ line_item.variant.unit_to_display }} + / - if {{ line_item.product.name }} == {{ line_item.variant.name_to_display }} + %strong + {{ line_item.variant.name }} + {{ line_item.variant.name_to_display }} + %span.text-skinny{"ng-show" => "{{line_item.variant.unit_to_display}}"} + ({{ line_item.variant.unit_to_display }}) + + .columns.small-3.text-right %small %span.quantity {{ line_item.quantity }} From a8de4dc039fb89995a1679d0bc3d592b5f05bb46 Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 11:35:53 +1100 Subject: [PATCH 181/384] WIP checkout page and order confirmation page. Working on making these pages more consistent to each other, to email confirmation, and to shopping experience. --- app/views/spree/orders/_form.html.haml | 7 +- app/views/spree/orders/_line_item.html.haml | 13 +- app/views/spree/orders/show.html.haml | 10 +- .../spree/shared/_order_details.html.haml | 132 +++++++++--------- 4 files changed, 87 insertions(+), 75 deletions(-) diff --git a/app/views/spree/orders/_form.html.haml b/app/views/spree/orders/_form.html.haml index f0edc75221..6d43e97a92 100644 --- a/app/views/spree/orders/_form.html.haml +++ b/app/views/spree/orders/_form.html.haml @@ -3,8 +3,7 @@ .row .columns.large-12 %table#cart-detail{"data-hook" => ""} - / %col{halign: "center", valign: "middle", width: "30%"}/ - %col{halign: "left", valign: "middle"}/ + %col{halign: "left", valign: "middle", width: "60%"}/ %col{halign: "left", valign: "middle", width: "15%"}/ %col{halign: "center", valign: "middle", width: "10%"}/ %col{halign: "center", valign: "middle", width: "10%"}/ @@ -12,8 +11,8 @@ %thead %tr{"data-hook" => "cart_items_headers"} %th.cart-item-description-header= t(:item) - %th.cart-item-price-header= t(:price) - %th.cart-item-quantity-header= t(:qty) + %th.cart-item-price-header.text-right= t(:price) + %th.text-center.cart-item-quantity-header= t(:qty) %th.cart-item-total-header.text-right= t(:total) %th.cart-item-delete-header diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index 23af2320b8..53eb254812 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -1,5 +1,6 @@ %tr.line-item{class: "variant-#{variant.id}"} + / removed image thumbnail on shopping cart & checkout to simplify / %td.cart-item-image{"data-hook" => "cart_item_image"} / - if variant.images.length == 0 / = link_to small_image(variant.product), variant.product @@ -7,7 +8,13 @@ / = link_to image_tag(variant.images.first.attachment.url(:small)), variant.product %td.cart-item-description{'data-hook' => "cart_item_description"} - / %h4= variant.product.name + + %div.item-thumb-image{"data-hook" => "cart_item_image"} + - if variant.images.length == 0 + = link_to mini_image(variant.product), variant.product + - else + = link_to image_tag(variant.images.first.attachment.url(:mini)), variant.product + - if variant.product.name == variant.name_to_display %h5 %span= variant.product.name @@ -23,14 +30,14 @@ = variant.in_stock? ? t(:insufficient_stock, :on_hand => variant.on_hand) : t(:out_of_stock) %br/ - %td.cart-item-price{"data-hook" => "cart_item_price"} + %td.text-right.cart-item-price{"data-hook" => "cart_item_price"} = line_item.single_display_amount_with_adjustments.to_html -# Now in a template in app/assets/javascripts/templates ! %price-breakdown{"price-breakdown" => "_", variant: "variant", "price-breakdown-append-to-body" => "true", "price-breakdown-placement" => "left", "price-breakdown-animation" => true} - %td.cart-item-quantity{"data-hook" => "cart_item_quantity"} + %td.text-center.cart-item-quantity{"data-hook" => "cart_item_quantity"} = item_form.number_field :quantity, :min => 0, :class => "line_item_quantity", :size => 5 %td.cart-item-total.text-right{"data-hook" => "cart_item_total"} = line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil? diff --git a/app/views/spree/orders/show.html.haml b/app/views/spree/orders/show.html.haml index f47a3c2d36..0f8174240f 100644 --- a/app/views/spree/orders/show.html.haml +++ b/app/views/spree/orders/show.html.haml @@ -24,7 +24,9 @@ = render :partial => 'spree/shared/order_details', :locals => { :order => @order } - = link_to t(:back_to_store), main_app.shop_path, :class => "button" - - unless params.has_key? :checkout_complete - - if try_spree_current_user && respond_to?(:spree_account_path) - = link_to t(:my_account), spree_account_path, :class => "button" + .row + .columns.large-12 + = link_to t(:back_to_store), main_app.shop_path, :class => "button" + - unless params.has_key? :checkout_complete + - if try_spree_current_user && respond_to?(:spree_account_path) + = link_to t(:my_account), spree_account_path, :class => "button" diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index c66f8d9a92..d0175c830c 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -73,72 +73,76 @@ %br -%table#line-items{"data-hook" => "order_details"} - %col{halign: "center", valign: "middle", width: "15%"}/ - %col{valign: "middle", width: "70%"}/ - %col{halign: "center", valign: "middle", width: "5%"}/ - %col{halign: "center", valign: "middle", width: "5%"}/ - %col{halign: "center", valign: "middle", width: "5%"}/ - %thead{"data-hook" => ""} - %tr{"data-hook" => "order_details_line_items_headers"} - %th{colspan: "2"}= t(:item) - %th.price= t(:price) - %th.qty= t(:qty) - %th.total - %span= t(:total) - %tbody{"data-hook" => ""} - - @order.line_items.each do |item| - %tr{"data-hook" => "order_details_line_item_row"} - %td{"data-hook" => "order_item_image"} - - if item.variant.images.length == 0 - = link_to small_image(item.variant.product), item.variant.product - - else - = link_to image_tag(item.variant.images.first.attachment.url(:small)), item.variant.product - %td(data-hook = "order_item_description") - - if item.variant.product.name == item.variant.name_to_display - %h5= item.variant.product.name - - else - %h5 - %span= item.variant.product.name - %span= "- " + item.variant.name_to_display - %p= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? - %td.price{"data-hook" => "order_item_price"} - %span= item.single_display_amount_with_adjustments.to_html - %td{"data-hook" => "order_item_qty"}= item.quantity - %td.total{"data-hook" => "order_item_total"} - %span= item.display_amount_with_adjustments.to_html +.row + .columns.large-12 + %table#line-items{"data-hook" => "order_details"} + %col{valign: "middle"}/ + %col{halign: "center", valign: "middle", width: "5%"}/ + %col{halign: "center", valign: "middle", width: "5%"}/ + %col{halign: "center", valign: "middle", width: "5%"}/ + %thead{"data-hook" => ""} + %tr{"data-hook" => "order_details_line_items_headers"} + %th= t(:item) + %th.price= t(:price) + %th.text-center.qty= t(:qty) + %th.text-right.total + %span= t(:total) + %tbody{"data-hook" => ""} + - @order.line_items.each do |item| + %tr{"data-hook" => "order_details_line_item_row"} + %td(data-hook = "order_item_description") - %tfoot#order-total{"data-hook" => "order_details_total"} - %tr.total - %td{colspan: "4"} - %b - = t(:order_total) - \: - %td.total - %span#order_total= @order.display_total.to_html + %div.item-thumb-image + placeholder + - - if order.price_adjustment_totals.present? - %tfoot#price-adjustments{"data-hook" => "order_details_price_adjustments"} - - @order.price_adjustment_totals.each do |key, total| + - if item.variant.product.name == item.variant.name_to_display + %h5 + %span= item.variant.product.name + %span.text-small.text-skinny= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? + - else + %h5 + %span= item.variant.product.name + %span= "- " + item.variant.name_to_display + %span.text-small.text-skinny= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? + + %td.text-right.price{"data-hook" => "order_item_price"} + %span= item.single_display_amount_with_adjustments.to_html + %td.text-center{"data-hook" => "order_item_qty"}= item.quantity + %td.text-right.total{"data-hook" => "order_item_total"} + %span= item.display_amount_with_adjustments.to_html + + %tfoot#order-total{"data-hook" => "order_details_total"} %tr.total - %td{colspan: "4"} + %td.text-right{colspan: "3"} + %h5 + Total + %td.text-right.total + %h5#order_total= @order.display_total.to_html + + - if order.price_adjustment_totals.present? + %tfoot#price-adjustments{"data-hook" => "order_details_price_adjustments"} + - @order.price_adjustment_totals.each do |key, total| + %tr.total + %td.text-right{colspan: "3"} + %strong + = key + %td.text-right.total + %span= total + + %tfoot#subtotal{"data-hook" => "order_details_subtotal"} + %tr#subtotal-row.total + %td.text-right{colspan: "3"} %strong - = key - \: - %td.total - %span= total + Produce + %td.text-right.total + %span= display_checkout_subtotal(@order) - %tfoot#subtotal{"data-hook" => "order_details_subtotal"} - %tr#subtotal-row.total - %td{colspan: "4"} - Produce: - %td.total - %span= display_checkout_subtotal(@order) - - %tfoot#order-charges{"data-hook" => "order_details_adjustments"} - - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| - %tr.total - %td{:colspan => "4"} - = adjustment.label + ":" - %td.total - %span= adjustment.display_amount.to_html + %tfoot#order-charges{"data-hook" => "order_details_adjustments"} + - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| + %tr.total + %td.text-right{:colspan => "3"} + %strong + = adjustment.label + %td.text-right.total + %span= adjustment.display_amount.to_html From 91bf588bcc31c5acfbb1f47ca380c6aada77dc47 Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 12:03:21 +1100 Subject: [PATCH 182/384] Order details page add in mini thumbnail for product image --- app/views/spree/shared/_order_details.html.haml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index d0175c830c..c6a8090bf1 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -92,9 +92,12 @@ %tr{"data-hook" => "order_details_line_item_row"} %td(data-hook = "order_item_description") - %div.item-thumb-image - placeholder - + %div.item-thumb-image{"data-hook" => "order_item_image"} + - if item.variant.images.length == 0 + = link_to mini_image(item.variant.product), item.variant.product + - else + = link_to image_tag(item.variant.images.first.attachment.url(:mini)), item.variant.product + - if item.variant.product.name == item.variant.name_to_display %h5 From 21b18a8cc54c482bc12dcea2e76e7fafb8dd8855 Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 12:31:53 +1100 Subject: [PATCH 183/384] WIP commit so i can see what i messed up. --- .../spree/shared/_order_details.html.haml | 82 ++++++++++--------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index c6a8090bf1..370e6fbe22 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -30,46 +30,54 @@ = order.bill_address.phone .columns.large-6 - .order-summary.text-small - %strong= order.shipping_method.name - - .pad - %p.text-big - - if @order.shipping_method.andand.require_ship_address - Delivery on - - else - Ready for collection - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - %br - %span.text-small - %em= order.shipping_method.description.html_safe - .order-summary.text-small - %strong - - if @order.shipping_method.andand.require_ship_address + // Delivery option + - if @order.shipping_method.andand.require_ship_address + .order-summary.text-small + %strong= order.shipping_method.name + .pad + %p.text-big + Delivery on + %strong= @order.order_cycle.pickup_time_for(@order.distributor) + %br + %span.text-small + %em= order.shipping_method.description.html_safe + .order-summary.text-small + %strong Delivery address - - else - Collection Address - .pad - %p.text-small - - if @order.shipping_method.andand.require_ship_address + .pad + %p.text-small = order.ship_address.firstname + " " + order.ship_address.lastname %br - - else - / Do nothing - - = order.ship_address.full_address - %br - = order.ship_address.phone - - - if @order.shipping_method.andand.require_ship_address - / Do nothing - - else - / Collection instructions - - %p.light.small - %strong Your notes: - %br - = order.special_instructions + = order.ship_address.full_address + %br + = order.ship_address.phone + // Collection option + - else + .order-summary.text-small + %strong= order.shipping_method.name + .pad + %p.text-big + Ready for collection + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %br + %span.text-small + %em= order.shipping_method.description.html_safe + .order-summary.text-small + %strong + Collection Address + .pad + %p.text-small + = order.ship_address.full_address + .order-summary.text-small + %strong + Collection Instructions + .pad + %p.text-small #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + + %p.light.small + %strong Your notes: + %br + = order.special_instructions %br From 75b0b290aea1162fb488ebedae174c55d6a84794 Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 14:12:43 +1100 Subject: [PATCH 184/384] More WIP trying to work on the cart template logic with Rohan --- app/views/shared/menu/_cart.html.haml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/views/shared/menu/_cart.html.haml b/app/views/shared/menu/_cart.html.haml index ab733cdab5..b0e44f3bad 100644 --- a/app/views/shared/menu/_cart.html.haml +++ b/app/views/shared/menu/_cart.html.haml @@ -20,11 +20,7 @@ / %em {{ line_item.variant.unit_to_display }} / - if {{ line_item.product.name }} == {{ line_item.variant.name_to_display }} %strong - {{ line_item.variant.name }} {{ line_item.variant.name_to_display }} - %span.text-skinny{"ng-show" => "{{line_item.variant.unit_to_display}}"} - ({{ line_item.variant.unit_to_display }}) - .columns.small-3.text-right %small From f698408d408314012879ab04dfb5bcf1734578c9 Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 14:58:58 +1100 Subject: [PATCH 185/384] Logic for delivery vs collection - making template --- .../spree/shared/_order_details.html.haml | 64 ++++++++++--------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index 370e6fbe22..0c2bd81adb 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -11,13 +11,12 @@ %strong = order.display_total.to_html .pad - %p.text-big + .text-big Paying via: - %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe - %br - %span.text-small - %em - = order.payments.first.andand.payment_method.andand.description.andand.html_safe + %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe + %p.text-small.text-skinny.pre-line + %em= order.payments.first.andand.payment_method.andand.description.andand.html_safe + .order-summary.text-small %strong Billing address @@ -30,17 +29,16 @@ = order.bill_address.phone .columns.large-6 - // Delivery option - if @order.shipping_method.andand.require_ship_address + // Delivery option .order-summary.text-small %strong= order.shipping_method.name .pad - %p.text-big - Delivery on - %strong= @order.order_cycle.pickup_time_for(@order.distributor) - %br - %span.text-small - %em= order.shipping_method.description.html_safe + .text-big + Delivery on + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %p.text-small.text-skinny.pre-line + %em= order.shipping_method.description.html_safe .order-summary.text-small %strong Delivery address @@ -51,34 +49,42 @@ = order.ship_address.full_address %br = order.ship_address.phone - // Collection option + - if order.special_instructions + %br + %p.light.small + %strong Your notes: + %br + = order.special_instructions - else + // Collection option .order-summary.text-small %strong= order.shipping_method.name .pad - %p.text-big + .text-big Ready for collection %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - %br - %span.text-small - %em= order.shipping_method.description.html_safe + %p.text-small.text-skinny.pre-line + %em= order.shipping_method.description.html_safe .order-summary.text-small %strong Collection Address .pad %p.text-small = order.ship_address.full_address - .order-summary.text-small - %strong - Collection Instructions - .pad - %p.text-small #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - %p.light.small - %strong Your notes: - %br - = order.special_instructions - + + - if @order.order_cycle.pickup_instructions_for(@order.distributor) + %br + %p.text-small + %strong + Collection Instructions + %br + #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + - if order.special_instructions + %br + %p.light.small + %strong Your notes: + %br + = order.special_instructions %br .row From d967905b839f5c06b76adbc21b5a62f3dc7099b1 Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 14:59:26 +1100 Subject: [PATCH 186/384] Tweaking copy styles and removing references to Open Sans which not using. Silly laura mixing up repos --- .../stylesheets/darkswarm/typography.css.sass | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index fe0dbc8136..f549420d83 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -16,9 +16,8 @@ @font-face font-family: 'AvenirMed' src: url("/AvenirLTStd-Medium.otf") format("opentype") - -body - font-family: 'Open Sans', Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif + +$font-helvetica: "Helvetica Neue", "HelveticaNeue", "Helvetica", Helvetica, Arial, sans-serif a color: $clr-brick @@ -36,19 +35,26 @@ small, .small .text-small font-size: 0.875rem margin-bottom: 0.5rem + font-family: $font-helvetica &, & * font-size: 0.875rem .text-normal font-weight: 400 - font-family: 'Open Sans' + font-family: $font-helvetica .text-skinny font-weight: 300 - font-family: 'Open Sans' + font-family: $font-helvetica .word-wrap word-wrap: break-word + +.pre-wrap + white-space: pre-wrap + +.pre-line + white-space: pre-line .light color: #999 From ea96759306a2c64146ac9b9003ca05bac9c8f500 Mon Sep 17 00:00:00 2001 From: summerscope Date: Tue, 10 Mar 2015 14:59:46 +1100 Subject: [PATCH 187/384] Take styling out of template into SASS --- app/views/checkout/_payment.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/checkout/_payment.html.haml b/app/views/checkout/_payment.html.haml index 36775d6d35..cd3091efcd 100644 --- a/app/views/checkout/_payment.html.haml +++ b/app/views/checkout/_payment.html.haml @@ -35,4 +35,4 @@ = render partial: "spree/checkout/payment/#{method.method_type}", :locals => { :payment_method => method } .small-12.columns.medium-6.columns.large-6.columns #distributor_address.panel{"ng-show" => "Checkout.paymentMethod().description"} - %span{ style: "white-space: pre-wrap;" }{{ Checkout.paymentMethod().description }} + %span.pre-wrap {{ Checkout.paymentMethod().description }} From 2c89573441f96d8426ef788e708564294438d178 Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Wed, 11 Mar 2015 11:59:25 +1100 Subject: [PATCH 188/384] Shave off a few seconds when showing order_cycles#index by loading order_cycle_enterprises only once. --- app/controllers/admin/order_cycles_controller.rb | 1 + app/views/admin/order_cycles/_row.html.haml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 22a7aaa3cc..b629a2fd3b 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -84,6 +84,7 @@ module Admin private def load_order_cycle_set + @order_cycle_enterprises = OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises @order_cycle_set = OrderCycleSet.new :collection => collection end diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 6f1b389fd8..0c66fafa1e 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -1,5 +1,6 @@ - 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 @@ -7,12 +8,12 @@ - unless order_cycles_simple_index %td.suppliers - - order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |s| + - order_cycle.suppliers.merge(@order_cycle_enterprises).each do |s| = s.name %br/ %td= order_cycle.coordinator.name %td.distributors - - order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |d| + - order_cycle.distributors.merge(@order_cycle_enterprises).each do |d| = d.name %br/ From 208fa02ec01e49b18dcea520493b1428e70b2612 Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Wed, 11 Mar 2015 13:14:45 +1100 Subject: [PATCH 189/384] Create a 'Show More' button at the top of the page, next to New Order on on admin order cycles index page --- app/controllers/admin/order_cycles_controller.rb | 11 ++++++----- app/views/admin/order_cycles/index.html.haml | 6 ++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index b629a2fd3b..435be5b38e 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -5,7 +5,7 @@ module Admin class OrderCyclesController < ResourceController include OrderCyclesHelper - before_filter :load_order_cycle_set, :only => :index + before_filter :load_data_for_index, :only => :index before_filter :require_coordinator, only: :new def show @@ -73,19 +73,20 @@ module Admin protected - def collection + def collection(show_more=false) ocs = OrderCycle.managed_by(spree_current_user) ocs.undated + ocs.soonest_closing + ocs.soonest_opening + - ocs.recently_closed + (show_more ? ocs.closed : ocs.recently_closed) end private - def load_order_cycle_set + def load_data_for_index + @show_more = !!params[:show_more] @order_cycle_enterprises = OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises - @order_cycle_set = OrderCycleSet.new :collection => collection + @order_cycle_set = OrderCycleSet.new :collection => collection(@show_more) end def require_coordinator diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index afe18bc230..2afed8ed4c 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -4,6 +4,12 @@ = content_for :page_actions do %li#new_order_cycle_link = button_link_to "New Order Cycle", main_app.new_admin_order_cycle_path, :icon => 'icon-plus', :id => 'admin_new_order_cycle_link' + - if @show_more + %li + = button_link_to "Show less", main_app.admin_order_cycles_path + - else + %li + = button_link_to "Show more", main_app.admin_order_cycles_path(params: { show_more: true }) = form_for @order_cycle_set, :url => main_app.bulk_update_admin_order_cycles_path do |f| %table.index#listing_order_cycles From f90f7565fb70754211fc12625e93f61e50911250 Mon Sep 17 00:00:00 2001 From: Victor Nava Date: Wed, 11 Mar 2015 18:08:09 +1100 Subject: [PATCH 190/384] Allow admins to delete Order Cycles --- app/helpers/order_cycles_helper.rb | 4 ++++ app/views/admin/order_cycles/_row.html.haml | 5 ++++- spec/features/admin/order_cycles_spec.rb | 9 +++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 6e401af0a6..fb523f2c98 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -75,6 +75,10 @@ module OrderCyclesHelper order_cycle.exchanges.to_enterprises(current_distributor).outgoing.first.pickup_time end + def can_delete?(order_cycle) + Spree::Order.where(order_cycle_id: order_cycle).none? + end + private def validated_enterprise_options(enterprises, options={}) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 0c66fafa1e..f5c8ebafbe 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -23,4 +23,7 @@ %td.actions = link_to '', main_app.edit_admin_order_cycle_path(order_cycle), class: 'edit-order-cycle icon-edit no-text' %td.actions - = link_to '', main_app.clone_admin_order_cycle_path(order_cycle), class: 'clone-order-cycle icon-copy no-text' + = link_to '', main_app.clone_admin_order_cycle_path(order_cycle), class: 'clone-order-cycle icon-copy no-text' + - if can_delete?(order_cycle) + %td.actions + = link_to '', main_app.admin_order_cycle_path(order_cycle), class: 'delete-product icon-trash no-text', :method => :delete, data: { confirm: "Are you sure?" } diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 2597417b75..b0a63e5b5e 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -777,6 +777,15 @@ feature %q{ end end + scenario "deleting an order cycle" do + create(:simple_order_cycle, name: "Translusent Berries") + login_to_admin_section + click_link 'Order Cycles' + page.should have_content("Translusent Berries") + first('a.delete-product').click + page.should_not have_content("Translusent Berries") + end + private From 1e5e009735c67caf6807321ff57b8b75a6deeb86 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 12 Mar 2015 11:29:01 +1100 Subject: [PATCH 191/384] Spree init works when database has not been created, remove duplicate FK from db/schema.rb --- config/initializers/spree.rb | 8 ++++++-- db/schema.rb | 3 --- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/config/initializers/spree.rb b/config/initializers/spree.rb index 5912e0b9ec..9c81da88ef 100644 --- a/config/initializers/spree.rb +++ b/config/initializers/spree.rb @@ -14,8 +14,12 @@ Spree.config do |config| config.checkout_zone = ENV["CHECKOUT_ZONE"] config.address_requires_state = true - country = Spree::Country.find_by_name(ENV["DEFAULT_COUNTRY"]) - config.default_country_id = country.id if country.present? + if Spree::Country.table_exists? + country = Spree::Country.find_by_name(ENV["DEFAULT_COUNTRY"]) + config.default_country_id = country.id if country.present? + else + config.default_country_id = 12 # Australia + end # -- spree_paypal_express # Auto-capture payments. Without this option, payments must be manually captured in the paypal interface. diff --git a/db/schema.rb b/db/schema.rb index a325b69f80..d885130600 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1096,9 +1096,6 @@ ActiveRecord::Schema.define(:version => 20150225232938) do add_foreign_key "enterprise_groups", "spree_addresses", name: "enterprise_groups_address_id_fk", column: "address_id" add_foreign_key "enterprise_groups", "spree_users", name: "enterprise_groups_owner_id_fk", column: "owner_id" - add_foreign_key "enterprise_groups", "spree_addresses", name: "enterprise_groups_address_id_fk", column: "address_id" - add_foreign_key "enterprise_groups", "spree_users", name: "enterprise_groups_owner_id_fk", column: "owner_id" - 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" From c4d4d1087363db7b74a85d374be4777299ae9eb7 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 12 Mar 2015 11:53:13 +1100 Subject: [PATCH 192/384] Reuse supplier/distributor list --- app/views/admin/order_cycles/_row.html.haml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index a80cb7948a..8c7ce2bbbd 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -8,26 +8,23 @@ - unless order_cycles_simple_index %td.suppliers - suppliers = order_cycle_permitted_in(order_cycle.suppliers) + - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 - - supplier_list = suppliers.map(&:name).sort.join ', ' %span.with-tip{'data-powertip' => supplier_list} = suppliers.count suppliers - else - - suppliers.each do |s| - = s.name - %br/ + = supplier_list %td= order_cycle.coordinator.name %td.distributors - distributors = order_cycle_permitted_in(order_cycle.distributors) + - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 - - distributor_list = distributors.map(&:name).sort.join ', ' %span.with-tip{'data-powertip' => distributor_list} = distributors.count distributors - else - - distributors.each do |d| - = d.name + = distributor_list %br/ %td.products From 1a97df1d21ae472c73cd5825b81cd7e5f7c25828 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 12 Mar 2015 12:17:15 +1100 Subject: [PATCH 193/384] Fix whitespace issues --- .../index/add_temp_controlled_td.html.haml.deface | 4 ++-- lib/open_food_network/order_cycle_management_report.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface b/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface index ce3d7304db..25337bdebd 100644 --- a/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface +++ b/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface @@ -1,5 +1,5 @@ / insert_after "[data-hook='category_row'] td:first-child" %td.align-center - = shipping_category.temperature_controlled - %br/ + = shipping_category.temperature_controlled + %br/ diff --git a/lib/open_food_network/order_cycle_management_report.rb b/lib/open_food_network/order_cycle_management_report.rb index 09b9449a82..b6e65f0a61 100644 --- a/lib/open_food_network/order_cycle_management_report.rb +++ b/lib/open_food_network/order_cycle_management_report.rb @@ -35,7 +35,7 @@ module OpenFoodNetwork private - def payment_method_row (order) + def payment_method_row(order) ba = order.billing_address da = order.distributor.andand.address [ba.firstname, @@ -51,7 +51,7 @@ module OpenFoodNetwork ] end - def delivery_row (order) + def delivery_row(order) ba = order.billing_address da = order.distributor.andand.address [ba.firstname, @@ -102,7 +102,7 @@ module OpenFoodNetwork params[:report_type] == "payment_methods" end - def customer_code (email) + def customer_code(email) customer = Customer.where(email: email).first customer.nil? ? "" : customer.code end From c85d5b86ebadb324a63ad9a0f0ed8551be9fdaec Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 12 Mar 2015 12:22:51 +1100 Subject: [PATCH 194/384] Fix temperature_controlled migration - NOT NULL requires default value --- ...d_temperature_controlled_to_spree_shipping_categories.rb | 2 +- db/schema.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb index 9ddf19c81e..89035fc16f 100644 --- a/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb +++ b/db/migrate/20150216075336_add_temperature_controlled_to_spree_shipping_categories.rb @@ -1,5 +1,5 @@ class AddTemperatureControlledToSpreeShippingCategories < ActiveRecord::Migration def change - add_column :spree_shipping_categories, :temperature_controlled, :boolean, null: false + add_column :spree_shipping_categories, :temperature_controlled, :boolean, null: false, default: false end end diff --git a/db/schema.rb b/db/schema.rb index 3362ff7455..7435fa4e30 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -864,9 +864,9 @@ ActiveRecord::Schema.define(:version => 20150225232938) do create_table "spree_shipping_categories", :force => true do |t| t.string "name" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.boolean "temperature_controlled", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "temperature_controlled", :default => false, :null => false end create_table "spree_shipping_methods", :force => true do |t| From 7c723c04b6430157a12aeda6d2c2a0ac07bbf46d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 12 Mar 2015 12:23:02 +1100 Subject: [PATCH 195/384] Show yes/no instead of true/false --- .../index/add_temp_controlled_td.html.haml.deface | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface b/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface index 25337bdebd..aaafec5607 100644 --- a/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface +++ b/app/overrides/spree/admin/shipping_categories/index/add_temp_controlled_td.html.haml.deface @@ -1,5 +1,5 @@ / insert_after "[data-hook='category_row'] td:first-child" %td.align-center - = shipping_category.temperature_controlled + = shipping_category.temperature_controlled ? 'Yes' : 'No' %br/ From 36fa4896ed0fb59ab2f29004f7217c3da9abff2b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 12 Mar 2015 12:28:35 +1100 Subject: [PATCH 196/384] Syntax --- .../add_temperature_controlled_form_element.html.haml.deface | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/overrides/spree/admin/shipping_categories/_form/add_temperature_controlled_form_element.html.haml.deface b/app/overrides/spree/admin/shipping_categories/_form/add_temperature_controlled_form_element.html.haml.deface index 4e9652bbd3..d447471961 100644 --- a/app/overrides/spree/admin/shipping_categories/_form/add_temperature_controlled_form_element.html.haml.deface +++ b/app/overrides/spree/admin/shipping_categories/_form/add_temperature_controlled_form_element.html.haml.deface @@ -1,5 +1,5 @@ / insert_bottom "div[data-hook='admin_shipping_category_form_fields']" -%div.field.align-center{data-hook => "name"} +%div.field.align-center{"data-hook" => "name"} = f.label :temperature_controlled, t(:temperature_controlled) = f.check_box :temperature_controlled From eb413ccc840403aef94864d85ccdb8e4b00f8347 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 12 Mar 2015 16:18:07 +1100 Subject: [PATCH 197/384] Committing schema changes --- db/schema.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index a325b69f80..d885130600 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1096,9 +1096,6 @@ ActiveRecord::Schema.define(:version => 20150225232938) do add_foreign_key "enterprise_groups", "spree_addresses", name: "enterprise_groups_address_id_fk", column: "address_id" add_foreign_key "enterprise_groups", "spree_users", name: "enterprise_groups_owner_id_fk", column: "owner_id" - add_foreign_key "enterprise_groups", "spree_addresses", name: "enterprise_groups_address_id_fk", column: "address_id" - add_foreign_key "enterprise_groups", "spree_users", name: "enterprise_groups_owner_id_fk", column: "owner_id" - 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" From 44511b8b6137e1faf37b3c2eb32eb43279ccc053 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 12 Mar 2015 16:18:30 +1100 Subject: [PATCH 198/384] Only display shipping description if it exists --- .../spree/shared/_order_details.html.haml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index 0c2bd81adb..0fdcf33a82 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -6,21 +6,21 @@ PAID - else NOT PAID - %span + %span Total order %strong = order.display_total.to_html .pad .text-big Paying via: - %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe + %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe %p.text-small.text-skinny.pre-line %em= order.payments.first.andand.payment_method.andand.description.andand.html_safe - + .order-summary.text-small %strong Billing address - .pad + .pad %p.text-small = order.bill_address.firstname + " " + order.bill_address.lastname %br @@ -33,16 +33,16 @@ // Delivery option .order-summary.text-small %strong= order.shipping_method.name - .pad + .pad .text-big Delivery on %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} %p.text-small.text-skinny.pre-line - %em= order.shipping_method.description.html_safe + %em= order.shipping_method.description.andand.html_safe || "" .order-summary.text-small %strong Delivery address - .pad + .pad %p.text-small = order.ship_address.firstname + " " + order.ship_address.lastname %br @@ -59,24 +59,24 @@ // Collection option .order-summary.text-small %strong= order.shipping_method.name - .pad + .pad .text-big - Ready for collection + Ready for collection %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} %p.text-small.text-skinny.pre-line - %em= order.shipping_method.description.html_safe + %em= order.shipping_method.description.andand.html_safe || "" .order-summary.text-small - %strong + %strong Collection Address - .pad + .pad %p.text-small = order.ship_address.full_address - + - if @order.order_cycle.pickup_instructions_for(@order.distributor) %br %p.text-small - %strong - Collection Instructions + %strong + Collection Instructions %br #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - if order.special_instructions @@ -85,7 +85,7 @@ %strong Your notes: %br = order.special_instructions - + %br .row .columns.large-12 @@ -111,7 +111,7 @@ = link_to mini_image(item.variant.product), item.variant.product - else = link_to image_tag(item.variant.images.first.attachment.url(:mini)), item.variant.product - + - if item.variant.product.name == item.variant.name_to_display %h5 @@ -122,7 +122,7 @@ %span= item.variant.product.name %span= "- " + item.variant.name_to_display %span.text-small.text-skinny= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? - + %td.text-right.price{"data-hook" => "order_item_price"} %span= item.single_display_amount_with_adjustments.to_html %td.text-center{"data-hook" => "order_item_qty"}= item.quantity From d9e8ce2f0e651861000f3115817b19c9a2c4309d Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Thu, 12 Mar 2015 17:16:51 +1100 Subject: [PATCH 199/384] Fix bug in report that selecting multiple payment methods didn't work. Added spec as well --- app/models/spree/order_decorator.rb | 2 +- spec/models/spree/order_spec.rb | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index c4dfeaeb64..5d3e317572 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -68,7 +68,7 @@ Spree::Order.class_eval do scope :with_payment_method_name, lambda { |payment_method_name| joins(:payments => :payment_method). - where('spree_payment_methods.name = ?', payment_method_name). + where('spree_payment_methods.name IN (?)', payment_method_name). select('DISTINCT spree_orders.*') } diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 114f774a1d..ae689e009e 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -337,7 +337,7 @@ describe Spree::Order do end end - describe "with payment method name" do + describe "with payment method names" do let!(:o1) { create(:order) } let!(:o2) { create(:order) } let!(:pm1) { create(:payment_method, name: 'foo') } @@ -345,8 +345,12 @@ describe Spree::Order do let!(:p1) { create(:payment, order: o1, payment_method: pm1) } let!(:p2) { create(:payment, order: o2, payment_method: pm2) } - it "returns the order with payment method name" do - Spree::Order.with_payment_method_name('foo').should == [o1] + it "returns the order with payment method name when one specified" do + Spree::Order.with_payment_method_name('foo').should == [o1] + end + + it "returns the orders with payment method name when many specified" do + Spree::Order.with_payment_method_name(['foo', 'bar']).should include o1, o2 end it "doesn't return rows with a different payment method name" do @@ -356,7 +360,7 @@ describe Spree::Order do it "doesn't return duplicate rows" do p2 = FactoryGirl.create(:payment, :order => o1, :payment_method => pm1) - Spree::Order.with_payment_method_name('foo').length.should == 1 + Spree::Order.with_payment_method_name('foo').length.should == 1 end end end From 5609354136314d41daf8d2636bbd4cbf8beb4302 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 12 Mar 2015 14:54:20 +1100 Subject: [PATCH 200/384] Fix bug: When there are variant overrides not owned by the current user, user is unable to make any changes to VOs --- .../admin/variant_overrides_controller.rb | 26 ++++++++++++------- app/models/variant_override_set.rb | 4 +-- spec/features/admin/variant_overrides_spec.rb | 2 ++ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app/controllers/admin/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index 6724896eb9..c89a8e8dd2 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -6,23 +6,15 @@ module Admin include OpenFoodNetwork::SpreeApiKeyLoader before_filter :load_spree_api_key, only: :index + before_filter :load_data def index - @hubs = order_cycle_hub_enterprises - - # Used in JS to look up the name of the producer of each product - @producers = OpenFoodNetwork::Permissions.new(spree_current_user). - variant_override_producers - - @hub_permissions = OpenFoodNetwork::Permissions.new(spree_current_user). - variant_override_enterprises_per_hub - @variant_overrides = VariantOverride.for_hubs(@hubs) end def bulk_update collection_hash = Hash[params[:variant_overrides].each_with_index.map { |vo, i| [i, vo] }] - vo_set = VariantOverrideSet.new collection_attributes: collection_hash + vo_set = VariantOverrideSet.new @variant_overrides, collection_attributes: collection_hash # Ensure we're authorised to update all variant overrides vo_set.collection.each { |vo| authorize! :update, vo } @@ -40,6 +32,20 @@ module Admin end + private + + def load_data + @hubs = order_cycle_hub_enterprises + + # Used in JS to look up the name of the producer of each product + @producers = OpenFoodNetwork::Permissions.new(spree_current_user). + variant_override_producers + + @hub_permissions = OpenFoodNetwork::Permissions.new(spree_current_user). + variant_override_enterprises_per_hub + @variant_overrides = VariantOverride.for_hubs(@hubs) + end + def collection end end diff --git a/app/models/variant_override_set.rb b/app/models/variant_override_set.rb index fc02173862..985190095b 100644 --- a/app/models/variant_override_set.rb +++ b/app/models/variant_override_set.rb @@ -1,6 +1,6 @@ class VariantOverrideSet < ModelSet - def initialize(attributes={}) - super(VariantOverride, VariantOverride.all, attributes, nil, + def initialize(collection, attributes={}) + super(VariantOverride, collection, attributes, nil, proc { |attrs| attrs['price'].blank? && attrs['count_on_hand'].blank? } ) end end diff --git a/spec/features/admin/variant_overrides_spec.rb b/spec/features/admin/variant_overrides_spec.rb index 886f7d7e5b..ee615f44f5 100644 --- a/spec/features/admin/variant_overrides_spec.rb +++ b/spec/features/admin/variant_overrides_spec.rb @@ -11,6 +11,7 @@ feature %q{ let!(:hub) { create(:distributor_enterprise) } let!(:hub2) { create(:distributor_enterprise) } + let!(:hub3) { create(:distributor_enterprise) } let!(:producer) { create(:supplier_enterprise) } let!(:er1) { create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) } @@ -156,6 +157,7 @@ feature %q{ context "with overrides" do let!(:vo) { create(:variant_override, variant: variant, hub: hub, price: 77.77, count_on_hand: 11111) } + let!(:vo_no_auth) { create(:variant_override, variant: variant, hub: hub3, price: 1, count_on_hand: 2) } before do visit '/admin/variant_overrides' From d9f90be38af7e6e50e08613e93c412744d5b135a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 13 Mar 2015 10:52:44 +1100 Subject: [PATCH 201/384] Fix indentation --- spec/models/spree/order_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index ae689e009e..3eeac8b40f 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -346,11 +346,11 @@ describe Spree::Order do let!(:p2) { create(:payment, order: o2, payment_method: pm2) } it "returns the order with payment method name when one specified" do - Spree::Order.with_payment_method_name('foo').should == [o1] + Spree::Order.with_payment_method_name('foo').should == [o1] end it "returns the orders with payment method name when many specified" do - Spree::Order.with_payment_method_name(['foo', 'bar']).should include o1, o2 + Spree::Order.with_payment_method_name(['foo', 'bar']).should include o1, o2 end it "doesn't return rows with a different payment method name" do @@ -359,8 +359,8 @@ describe Spree::Order do end it "doesn't return duplicate rows" do - p2 = FactoryGirl.create(:payment, :order => o1, :payment_method => pm1) - Spree::Order.with_payment_method_name('foo').length.should == 1 + p2 = FactoryGirl.create(:payment, order: o1, payment_method: pm1) + Spree::Order.with_payment_method_name('foo').length.should == 1 end end end From efbf2c7ffaa9dbe438939b87b807d63ee4eb7039 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 13 Mar 2015 12:58:53 +1100 Subject: [PATCH 202/384] Display extended variant name in quick cart --- .../darkswarm/services/cart.js.coffee | 11 +++++++- app/serializers/api/variant_serializer.rb | 7 ++++- app/views/shared/menu/_cart.html.haml | 2 +- .../darkswarm/services/cart_spec.js.coffee | 28 ++++++++++++++++++- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index c519d22fdd..0135416ac4 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -8,6 +8,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> for line_item in @line_items line_item.variant.line_item = line_item Variants.register line_item.variant + line_item.variant.extended_name = @extendedVariantName(line_item.variant) orderChanged: => @unsaved() @@ -67,4 +68,12 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> variant: variant quantity: null max_quantity: null - @line_items.push variant.line_item \ No newline at end of file + @line_items.push variant.line_item + + extendedVariantName: (variant) => + if variant.product_name == variant.name_to_display + variant.product_name + else + name = "#{variant.product_name} - #{variant.name_to_display}" + name += " (#{variant.options_text})" if variant.options_text + name diff --git a/app/serializers/api/variant_serializer.rb b/app/serializers/api/variant_serializer.rb index 5871d6def4..f995fa4345 100644 --- a/app/serializers/api/variant_serializer.rb +++ b/app/serializers/api/variant_serializer.rb @@ -1,6 +1,6 @@ class Api::VariantSerializer < ActiveModel::Serializer attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display, - :on_demand, :price, :fees, :price_with_fees + :options_text, :on_demand, :price, :fees, :price_with_fees, :product_name def price_with_fees object.price_with_fees(options[:current_distributor], options[:current_order_cycle]) @@ -13,4 +13,9 @@ class Api::VariantSerializer < ActiveModel::Serializer def fees object.fees_by_type_for(options[:current_distributor], options[:current_order_cycle]) end + + def product_name + object.product.name + end + end diff --git a/app/views/shared/menu/_cart.html.haml b/app/views/shared/menu/_cart.html.haml index b0e44f3bad..716d198f67 100644 --- a/app/views/shared/menu/_cart.html.haml +++ b/app/views/shared/menu/_cart.html.haml @@ -20,7 +20,7 @@ / %em {{ line_item.variant.unit_to_display }} / - if {{ line_item.product.name }} == {{ line_item.variant.name_to_display }} %strong - {{ line_item.variant.name_to_display }} + {{ line_item.variant.extended_name }} .columns.small-3.text-right %small diff --git a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee index 4d13ccc8b5..4be1a13dd8 100644 --- a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee @@ -6,7 +6,10 @@ describe 'Cart service', -> beforeEach -> module 'Darkswarm' - variant = {id: 1} + variant = + id: 1 + name_to_display: 'name' + product_name: 'name' order = { line_items: [ variant: variant @@ -23,6 +26,9 @@ describe 'Cart service', -> it "registers variants with the Variants service", -> expect(Variants.variants[1]).toBe variant + it "generates extended variant names", -> + expect(Cart.line_items[0].variant.extended_name).toEqual "name" + it "creates and backreferences new line items if necessary", -> Cart.register_variant(v2 = {id: 2}) expect(Cart.line_items[1].variant).toBe v2 @@ -37,3 +43,23 @@ describe 'Cart service', -> expect(Cart.line_items_present()).toEqual [] order.line_items[0].quantity = 2 expect(Cart.total_item_count()).toEqual 2 + + describe "generating an extended variant name", -> + it "returns the product name when it is the same as the variant name", -> + variant = {product_name: 'product_name', name_to_display: 'product_name'} + expect(Cart.extendedVariantName(variant)).toEqual "product_name" + + describe "when the product name and the variant name differ", -> + it "returns a combined name when there is no options text", -> + variant = + product_name: 'product_name' + name_to_display: 'name_to_display' + expect(Cart.extendedVariantName(variant)).toEqual "product_name - name_to_display" + + it "returns a combined name when there is some options text", -> + variant = + product_name: 'product_name' + name_to_display: 'name_to_display' + options_text: 'options_text' + + expect(Cart.extendedVariantName(variant)).toEqual "product_name - name_to_display (options_text)" From 1a44e74a9d3ed6e7e79dc1feb85796cc9a84ae82 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 13 Mar 2015 16:06:42 +1100 Subject: [PATCH 203/384] When enterprise user creates an enterprise, make it a hub if they have other hubs --- .../admin/enterprises_controller.rb | 9 +++ .../admin/enterprises_controller_spec.rb | 71 ++++++++++++++----- spec/factories.rb | 14 +++- .../request/authentication_workflow.rb | 1 + 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index aa8276020d..37cf4ff09a 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -7,10 +7,12 @@ module Admin before_filter :check_can_change_sells, only: :update before_filter :check_can_change_bulk_sells, only: :bulk_update before_filter :override_owner, only: :create + before_filter :override_sells, only: :create before_filter :check_can_change_owner, only: :update before_filter :check_can_change_bulk_owner, only: :bulk_update before_filter :check_can_change_managers, only: :update + helper 'spree/products' include OrderCyclesHelper @@ -119,6 +121,13 @@ module Admin params[:enterprise][:owner_id] = spree_current_user.id unless spree_current_user.admin? end + def override_sells + unless spree_current_user.admin? + has_hub = spree_current_user.enterprises.is_hub.any? + params[:enterprise][:sells] = has_hub ? 'any' : 'none' + end + end + def check_can_change_owner unless ( spree_current_user == @enterprise.owner ) || spree_current_user.admin? params[:enterprise].delete :owner_id diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 5b3b182842..86b128af38 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -3,25 +3,16 @@ require 'spec_helper' module Admin describe EnterprisesController do include AuthenticationWorkflow - let(:user) { create_enterprise_user } - let(:distributor_manager) do - user = create(:user) - user.spree_roles = [] - distributor.enterprise_roles.build(user: user).save - user - end - let(:distributor_owner) do - user = create(:user) - user.spree_roles = [] - user - end - let(:admin_user) do - user = create(:user) - user.spree_roles << Spree::Role.find_or_create_by_name!('admin') - user - end + + let(:user) { create(:user) } + let(:admin_user) { create(:admin_user) } + let(:distributor_manager) { create(:user, enterprise_limit: 10, enterprises: [distributor]) } + let(:supplier_manager) { create(:user, enterprise_limit: 10, enterprises: [supplier]) } + let(:distributor_owner) { create(:user) } + let(:supplier_owner) { create(:user) } let(:distributor) { create(:distributor_enterprise, owner: distributor_owner ) } + let(:supplier) { create(:supplier_enterprise, owner: supplier_owner) } describe "creating an enterprise" do @@ -47,7 +38,7 @@ module Admin admin_user.enterprise_roles.where(enterprise_id: enterprise).should be_empty end - it "it overrides the owner_id submitted by the user unless current_user is super admin" do + it "overrides the owner_id submitted by the user unless current_user is super admin" do controller.stub spree_current_user: distributor_manager enterprise_params[:enterprise][:owner_id] = user @@ -55,6 +46,50 @@ module Admin enterprise = Enterprise.find_by_name 'zzz' distributor_manager.enterprise_roles.where(enterprise_id: enterprise).first.should be end + + context "when I already have a hub" do + it "creates the new enterprise as a hub" do + controller.stub spree_current_user: distributor_manager + enterprise_params[:enterprise][:owner_id] = distributor_manager + + spree_put :create, enterprise_params + enterprise = Enterprise.find_by_name 'zzz' + enterprise.sells.should == 'any' + end + + it "doesn't affect the hub status for super admins" do + admin_user.enterprises << create(:distributor_enterprise) + + controller.stub spree_current_user: admin_user + enterprise_params[:enterprise][:owner_id] = admin_user + enterprise_params[:enterprise][:sells] = 'none' + + spree_put :create, enterprise_params + enterprise = Enterprise.find_by_name 'zzz' + enterprise.sells.should == 'none' + end + end + + context "when I do not have a hub" do + it "does not create the new enterprise as a hub" do + controller.stub spree_current_user: supplier_manager + enterprise_params[:enterprise][:owner_id] = supplier_manager + + spree_put :create, enterprise_params + enterprise = Enterprise.find_by_name 'zzz' + enterprise.sells.should == 'none' + end + + it "doesn't affect the hub status for super admins" do + controller.stub spree_current_user: admin_user + enterprise_params[:enterprise][:owner_id] = admin_user + enterprise_params[:enterprise][:sells] = 'any' + + spree_put :create, enterprise_params + enterprise = Enterprise.find_by_name 'zzz' + enterprise.sells.should == 'any' + end + end end describe "updating an enterprise" do diff --git a/spec/factories.rb b/spec/factories.rb index 45e5d53647..7e25534941 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -219,7 +219,7 @@ FactoryGirl.modify do country { Spree::Country.find_by_name 'Australia' || Spree::Country.first } end - factory :payment do + factory :payment do ignore do distributor { order.distributor || Enterprise.is_distributor.first || FactoryGirl.create(:distributor_enterprise) } end @@ -234,6 +234,18 @@ FactoryGirl.modify do # Prevent inconsistent ordering in specs when all option types have the same (0) position sequence(:position) end + + factory :user do + after(:create) do |user| + user.spree_roles.clear # Remove admin role + end + end + + factory :admin_user do + after(:create) do |user| + user.spree_roles << Spree::Role.find_or_create_by_name!('admin') + end + end end diff --git a/spec/support/request/authentication_workflow.rb b/spec/support/request/authentication_workflow.rb index 39f78223d7..5e0c2cfb3d 100644 --- a/spec/support/request/authentication_workflow.rb +++ b/spec/support/request/authentication_workflow.rb @@ -1,5 +1,6 @@ module AuthenticationWorkflow include Warden::Test::Helpers + def quick_login_as(user) login_as user end From b0cfa6a17c391c2f6261bf8cd2943700e98314b9 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 13 Mar 2015 16:18:49 +1100 Subject: [PATCH 204/384] Shipping method prices displayed next to options in checkout --- app/views/checkout/_shipping.html.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/checkout/_shipping.html.haml b/app/views/checkout/_shipping.html.haml index d06da7931e..a1c6e5ba9c 100644 --- a/app/views/checkout/_shipping.html.haml +++ b/app/views/checkout/_shipping.html.haml @@ -21,10 +21,10 @@ "ng-value" => "method.id", "ng-model" => "order.shipping_method_id"} {{ method.name }} - // TODO: Laura - this is not working correctly, need to show shipping price regardless of if selected!? - %em.light{"ng-show" => "!Checkout.shippingPrice()"} (Free) - %em.light{"ng-show" => "order.shipping_method.andand.require_ship_address"} - {{ Checkout.shippingPrice() | localizeCurrency }} + %em.light{"ng-show" => "!method.price || method.price == 0"} + (Free) + %em.light{"ng-hide" => "!method.price || method.price == 0"} + ({{ method.price | localizeCurrency }}) %small.error.medium.input-text{"ng-show" => "!fieldValid('order.shipping_method_id')"} = "{{ fieldErrors('order.shipping_method_id') }}" From 864c9ec1da94492bc751092a804a2320bdf0952d Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 13 Mar 2015 16:49:26 +1100 Subject: [PATCH 205/384] This changes the user menu labels to show Account next to user email. Small tweak to user account page to make it less awful. --- .../stylesheets/darkswarm/checkout.css.sass | 1 + app/views/shared/_signed_in.html.haml | 7 +- .../shared/_signed_in_offcanvas.html.haml | 5 +- app/views/spree/users/show.html.haml | 65 ++++++++++--------- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/checkout.css.sass b/app/assets/stylesheets/darkswarm/checkout.css.sass index bcca5310f5..d5a2cb8104 100644 --- a/app/assets/stylesheets/darkswarm/checkout.css.sass +++ b/app/assets/stylesheets/darkswarm/checkout.css.sass @@ -5,6 +5,7 @@ .order-summary background-color: #e1f0f5 padding: 1em + width: 100% checkout display: block diff --git a/app/views/shared/_signed_in.html.haml b/app/views/shared/_signed_in.html.haml index 0d952dc4a8..69f0e61dbd 100644 --- a/app/views/shared/_signed_in.html.haml +++ b/app/views/shared/_signed_in.html.haml @@ -7,14 +7,15 @@ - if admin_user? or enterprise_user? %li - %a{href: spree.admin_path} + %a{href: spree.admin_path, target:'_blank'} %i.ofn-i_021-tools - Admin + Administration %li %a{href: spree.account_path} %i.ofn-i_015-user - = spree_current_user.email + Account + = "(" + spree_current_user.email + ")" %li %a{title: 'Log Out', href:'/logout' } diff --git a/app/views/shared/_signed_in_offcanvas.html.haml b/app/views/shared/_signed_in_offcanvas.html.haml index c063a885fe..97b3e1ebca 100644 --- a/app/views/shared/_signed_in_offcanvas.html.haml +++ b/app/views/shared/_signed_in_offcanvas.html.haml @@ -1,13 +1,14 @@ - if admin_user? or enterprise_user? %li - %a{href: spree.admin_path} + %a{href: spree.admin_path, target:'_blank'} %i.ofn-i_021-tools Admin %li %a{href: spree.account_path} %i.ofn-i_015-user - = spree_current_user.email + Account + / = spree_current_user.email %li %a{title: 'Log Out', href:'/logout' } diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index 49db903a14..878d271e42 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -1,33 +1,34 @@ .darkswarm - .row - %h1= accurate_title - .account-summary{"data-hook" => "account_summary"} - %dl#user-info - %dt= t(:email) - %dd - = @user.email - (#{link_to t(:edit), spree.edit_account_path}) - .account-my-orders{"data-hook" => "account_my_orders"} - %h3= t(:my_orders) - - if @orders.present? - %table.order-summary - %thead - %tr - %th.order-number= t(:order_number) - %th.order-date= t(:order_date) - %th.order-status= t(:status) - %th.order-payment-state= t(:payment_state) - %th.order-shipment-state= t(:shipment_state) - %th.order-total= t(:total) - %tbody - - @orders.each do |order| - %tr{class: cycle('even', 'odd')} - %td.order-number= link_to order.number, order_url(order) - %td.order-date= l order.completed_at.to_date - %td.order-status= t(order.state).titleize - %td.order-payment-state= t("payment_states.#{order.payment_state}") if order.payment_state - %td.order-shipment-state= t("shipment_states.#{order.shipment_state}") if order.shipment_state - %td.order-total= money order.total - - else - %p= t(:you_have_no_orders_yet) - %br/ + .row.pad-top + .small-12.columns.pad-top + %h1= accurate_title + .account-summary{"data-hook" => "account_summary"} + %dl#user-info + %dt= t(:email) + %dd + = @user.email + (#{link_to t(:edit), spree.edit_account_path}) + .account-my-orders{"data-hook" => "account_my_orders"} + %h3= t(:my_orders) + - if @orders.present? + %table.order-summary + %thead + %tr + %th.order-number= t(:order_number) + %th.order-date= t(:order_date) + %th.order-status= t(:status) + %th.order-payment-state= t(:payment_state) + %th.order-shipment-state= t(:shipment_state) + %th.order-total= t(:total) + %tbody + - @orders.each do |order| + %tr{class: cycle('even', 'odd')} + %td.order-number= link_to order.number, order_url(order) + %td.order-date= l order.completed_at.to_date + %td.order-status= t(order.state).titleize + %td.order-payment-state= t("payment_states.#{order.payment_state}") if order.payment_state + %td.order-shipment-state= t("shipment_states.#{order.shipment_state}") if order.shipment_state + %td.order-total= money order.total + - else + %p= t(:you_have_no_orders_yet) + %br/ From dd8c769ea9781835fe7591946742225654814068 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 13 Mar 2015 16:50:57 +1100 Subject: [PATCH 206/384] Hiding fee breakdown on cart page for now --- .../javascripts/darkswarm/cart.js.coffee | 6 +++--- app/views/spree/orders/_adjustments.html.haml | 18 ++++++++---------- app/views/spree/orders/_form.html.haml | 15 +++++++-------- app/views/spree/orders/_line_item.html.haml | 12 ++++++------ 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/darkswarm/cart.js.coffee b/app/assets/javascripts/darkswarm/cart.js.coffee index cd12c67c4a..471991143a 100644 --- a/app/assets/javascripts/darkswarm/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/cart.js.coffee @@ -11,9 +11,9 @@ $ -> # Temporarily handles the cart showing stuff $(document).ready -> - $('#cart_adjustments').hide() + $('.cart_adjustment').hide() - $('th.cart-adjustment-header a').click -> - $('#cart_adjustments').toggle() + $('td.cart-adjustments a').click -> + $('.cart_adjustment').toggle() $(this).html('Item Handling Fees (included in item totals)') false diff --git a/app/views/spree/orders/_adjustments.html.haml b/app/views/spree/orders/_adjustments.html.haml index b5956ba2cc..12b16375ec 100644 --- a/app/views/spree/orders/_adjustments.html.haml +++ b/app/views/spree/orders/_adjustments.html.haml @@ -1,11 +1,9 @@ -%thead - %tr{"data-hook" => "cart_adjustments_headers"} - %th.cart-adjustment-header{colspan: "5"} - %a{ href: "#" } Fees... +%tr{"data-hook" => "cart_adjustments_headers"} + %td.cart-adjustments{colspan: "5"} + %a{ href: "#" } Fees... -%tbody#cart_adjustments{"data-hook" => ""} - - checkout_line_item_adjustments(@order).each do |adjustment| - %tr - %td{colspan: "3"}= adjustment.label - %td.text-right= adjustment.display_amount.to_html - %td +- checkout_line_item_adjustments(@order).each do |adjustment| + %tr.cart_adjustment + %td{colspan: "3"}= adjustment.label + %td.text-right= adjustment.display_amount.to_html + %td diff --git a/app/views/spree/orders/_form.html.haml b/app/views/spree/orders/_form.html.haml index 6d43e97a92..bba45a53c8 100644 --- a/app/views/spree/orders/_form.html.haml +++ b/app/views/spree/orders/_form.html.haml @@ -19,7 +19,7 @@ %tbody#line_items{"data-hook" => ""} = order_form.fields_for :line_items do |item_form| = render :partial => 'line_item', :locals => { :variant => item_form.object.variant, :line_item => item_form.object, :item_form => item_form } - + %tfoot#edit-cart %tr %td{colspan:"2"} @@ -32,7 +32,12 @@ %span#clear_cart_link{"data-hook" => ""} = link_to "Empty cart", empty_cart_path, method: :put, :class => 'not-bold small' -#= form_tag empty_cart_path, :method => :put do - -#= submit_tag t(:empty_cart), :class => 'button alert expand small' + -#= submit_tag t(:empty_cart), :class => 'button alert expand small' + + / This is the fees row which we want to replace with the pop-over + -# - unless @order.adjustments.eligible.blank? + -# = render "spree/orders/adjustments" + %tr %td.text-right{colspan:"3"} Produce subtotal %td.text-right @@ -44,11 +49,6 @@ %td.text-right %span.order-total.distribution-total= display_checkout_admin_and_handling_adjustments_total_for(@order) %td - - / This is the fees row which we want to replace with the pop-over - / %tr - / %td{colspan:"5"} - / = render "spree/orders/adjustments" unless @order.adjustments.eligible.blank? %tr %td.text-right{colspan:"3"} @@ -56,4 +56,3 @@ %td.text-right %h5.order-total.grand-total= @order.display_total %td - diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index 53eb254812..d687290561 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -1,5 +1,5 @@ %tr.line-item{class: "variant-#{variant.id}"} - + / removed image thumbnail on shopping cart & checkout to simplify / %td.cart-item-image{"data-hook" => "cart_item_image"} / - if variant.images.length == 0 @@ -24,7 +24,7 @@ %span= variant.product.name %span= "- " + variant.name_to_display %span.text-small.text-skinny= " (" + variant.options_text + ")" unless variant.options_text.empty? - + - if @order.insufficient_stock_lines.include? line_item %span.out-of-stock = variant.in_stock? ? t(:insufficient_stock, :on_hand => variant.on_hand) : t(:out_of_stock) @@ -33,10 +33,10 @@ %td.text-right.cart-item-price{"data-hook" => "cart_item_price"} = line_item.single_display_amount_with_adjustments.to_html -# Now in a template in app/assets/javascripts/templates ! - %price-breakdown{"price-breakdown" => "_", variant: "variant", - "price-breakdown-append-to-body" => "true", - "price-breakdown-placement" => "left", - "price-breakdown-animation" => true} + -# %price-breakdown{"price-breakdown" => "_", variant: "variant", + -# "price-breakdown-append-to-body" => "true", + -# "price-breakdown-placement" => "left", + -# "price-breakdown-animation" => true} %td.text-center.cart-item-quantity{"data-hook" => "cart_item_quantity"} = item_form.number_field :quantity, :min => 0, :class => "line_item_quantity", :size => 5 %td.cart-item-total.text-right{"data-hook" => "cart_item_total"} From 8d3f25bb391ac758fb1736c04d1a7f723f3355c0 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 13 Mar 2015 17:50:01 +1100 Subject: [PATCH 207/384] Fix intermittent 500 error when updating the cart --- app/models/spree/tax_rate_decorator.rb | 1 + spec/models/spree/adjustment_spec.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/app/models/spree/tax_rate_decorator.rb b/app/models/spree/tax_rate_decorator.rb index e41c20db70..835c001a05 100644 --- a/app/models/spree/tax_rate_decorator.rb +++ b/app/models/spree/tax_rate_decorator.rb @@ -2,6 +2,7 @@ Spree::TaxRate.class_eval do def adjust_with_included_tax(order) adjust_without_included_tax(order) + order.reload (order.adjustments.tax + order.price_adjustments).each do |a| a.set_absolute_included_tax! a.amount end diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 35746b28fd..21475753be 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -23,6 +23,11 @@ module Spree adjustment.amount.should be > 0 adjustment.included_tax.should == adjustment.amount end + + it "does not crash when order data has been updated previously" do + order.price_adjustments.first.destroy + tax_rate.adjust(order) + end end describe "Shipment adjustments" do From d2fbf9f14d402879a0b040544d02908e77625f83 Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 13 Mar 2015 18:00:30 +1100 Subject: [PATCH 208/384] Fixing logic for instance where collection instructions are not entered --- .../confirm_email_for_customer.html.haml | 6 ++-- .../confirm_email_for_shop.html.haml | 6 ++-- .../spree/shared/_order_details.html.haml | 29 ++++++++++--------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index c769615b0b..4488c76edb 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -123,12 +123,12 @@ - else Collection details - - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + - if @order.order_cycle.andand.pickup_time_for(@order.distributor).present? %h4 Ready for collection: %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - - if @order.shipping_method.andand.description + - if @order.shipping_method.andand.description.present? %p %em #{@order.shipping_method.description.html_safe} %br   @@ -139,7 +139,7 @@ %br #{@order.ship_address.full_address} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor).present? %p %strong Collection instructions: %br diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index a99c448c6f..fe48c1c6d1 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -122,12 +122,12 @@ - else Collection details - - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + - if @order.order_cycle.andand.pickup_time_for(@order.distributor).present? %h4 Ready for collection: %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - - if @order.shipping_method.andand.description + - if @order.shipping_method.andand.description.present? %p %em #{@order.shipping_method.description.html_safe} %br   @@ -138,7 +138,7 @@ %br #{@order.ship_address.full_address} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor).present? %p %strong Collection instructions: %br diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index 0fdcf33a82..da7bc84ca1 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -2,7 +2,7 @@ .columns.large-6 .order-summary.text-small .right - - if @order.paid? + - if order.paid? PAID - else NOT PAID @@ -13,7 +13,7 @@ .pad .text-big Paying via: - %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe + %strong= order.payments.first.andand.payment_method.andand.name.andand.html_safe %p.text-small.text-skinny.pre-line %em= order.payments.first.andand.payment_method.andand.description.andand.html_safe @@ -29,14 +29,14 @@ = order.bill_address.phone .columns.large-6 - - if @order.shipping_method.andand.require_ship_address + - if order.shipping_method.andand.require_ship_address // Delivery option .order-summary.text-small %strong= order.shipping_method.name .pad .text-big Delivery on - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %strong #{order.order_cycle.pickup_time_for(order.distributor)} %p.text-small.text-skinny.pre-line %em= order.shipping_method.description.andand.html_safe || "" .order-summary.text-small @@ -49,7 +49,7 @@ = order.ship_address.full_address %br = order.ship_address.phone - - if order.special_instructions + - if order.special_instructions.present? %br %p.light.small %strong Your notes: @@ -62,7 +62,7 @@ .pad .text-big Ready for collection - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + %strong #{order.order_cycle.pickup_time_for(order.distributor)} %p.text-small.text-skinny.pre-line %em= order.shipping_method.description.andand.html_safe || "" .order-summary.text-small @@ -72,14 +72,15 @@ %p.text-small = order.ship_address.full_address - - if @order.order_cycle.pickup_instructions_for(@order.distributor) + - if order.order_cycle.pickup_instructions_for(order.distributor).present? %br %p.text-small %strong Collection Instructions %br - #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - - if order.special_instructions + #{order.order_cycle.pickup_instructions_for(order.distributor)} + + - if order.special_instructions.present? %br %p.light.small %strong Your notes: @@ -102,7 +103,7 @@ %th.text-right.total %span= t(:total) %tbody{"data-hook" => ""} - - @order.line_items.each do |item| + - order.line_items.each do |item| %tr{"data-hook" => "order_details_line_item_row"} %td(data-hook = "order_item_description") @@ -135,11 +136,11 @@ %h5 Total %td.text-right.total - %h5#order_total= @order.display_total.to_html + %h5#order_total= order.display_total.to_html - if order.price_adjustment_totals.present? %tfoot#price-adjustments{"data-hook" => "order_details_price_adjustments"} - - @order.price_adjustment_totals.each do |key, total| + - order.price_adjustment_totals.each do |key, total| %tr.total %td.text-right{colspan: "3"} %strong @@ -153,10 +154,10 @@ %strong Produce %td.text-right.total - %span= display_checkout_subtotal(@order) + %span= display_checkout_subtotal(order) %tfoot#order-charges{"data-hook" => "order_details_adjustments"} - - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| + - checkout_adjustments_for(order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| %tr.total %td.text-right{:colspan => "3"} %strong From d0f66a6053fb36c1bfc648ac384362d3f5f79c1c Mon Sep 17 00:00:00 2001 From: summerscope Date: Fri, 13 Mar 2015 18:10:59 +1100 Subject: [PATCH 209/384] Fixing label for cart popover to make it write name of product variant without page load --- app/assets/javascripts/darkswarm/services/cart.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index 0135416ac4..bbb0b5fab4 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -64,6 +64,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> @create_line_item(variant) unless exists create_line_item: (variant)-> + variant.extended_name = @extendedVariantName(variant) variant.line_item = variant: variant quantity: null From cb28843af13f6e3f3f04ffadefd0202c8ee50f33 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 14 Mar 2015 10:41:49 +1100 Subject: [PATCH 210/384] Reload manager before ensuring owner is a manager --- app/models/enterprise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 462910aa0d..6725620526 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -360,7 +360,7 @@ class Enterprise < ActiveRecord::Base end def ensure_owner_is_manager - users << owner unless users.include?(owner) || owner.admin? + users << owner unless users(:reload).include?(owner) || owner.admin? end def enforce_ownership_limit From 8b241f058b5979f5823a567ede45fbc6809a996a Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 14 Mar 2015 11:02:45 +1100 Subject: [PATCH 211/384] Login to consumer section in a way that ensures user is not an admin --- spec/support/request/authentication_workflow.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spec/support/request/authentication_workflow.rb b/spec/support/request/authentication_workflow.rb index 5e0c2cfb3d..0995367600 100644 --- a/spec/support/request/authentication_workflow.rb +++ b/spec/support/request/authentication_workflow.rb @@ -38,6 +38,7 @@ module AuthenticationWorkflow visit spree.admin_path end + # TODO: Should probably just rename this to create_user 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. @@ -55,11 +56,8 @@ module AuthenticationWorkflow end def login_to_consumer_section - # The first user is given the admin role by Spree, so create a dummy user if this is the first - create(:user) if Spree::User.admin.empty? - user_role = Spree::Role.find_or_create_by_name!('user') - user = Spree::User.create!({ + user = create_enterprise_user({ :email => 'someone@ofn.org', :password => 'passw0rd', :password_confirmation => 'passw0rd', From 20d59cf5a540e7427615a0f56181f1c3c9fb4696 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 14 Mar 2015 11:40:21 +1100 Subject: [PATCH 212/384] Fixing authentication spec to work with altered user factory --- spec/features/admin/authentication_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/features/admin/authentication_spec.rb b/spec/features/admin/authentication_spec.rb index 12e9795ad2..059963fd70 100644 --- a/spec/features/admin/authentication_spec.rb +++ b/spec/features/admin/authentication_spec.rb @@ -6,6 +6,7 @@ feature "Authentication", js: true do include WebHelper let(:user) { create(:user, password: "password", password_confirmation: "password") } + let!(:enterprise) { create(:enterprise, owner: user) } # Required for access to admin scenario "logging into admin redirects home, then back to admin" do # This is the first admin spec, so give a little extra load time for slow systems @@ -16,7 +17,7 @@ feature "Authentication", js: true do fill_in "Email", with: user.email fill_in "Password", with: user.password click_login_button - page.should have_content "Dashboard" + page.should have_content "DASHBOARD" current_path.should == spree.admin_path end From bdeca54377d768e650033d06adefba74b37c4622 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 14 Mar 2015 16:17:16 +1100 Subject: [PATCH 213/384] Reloading stale enterprise object in test --- app/models/enterprise.rb | 2 +- spec/features/admin/enterprises_spec.rb | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 6725620526..462910aa0d 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -360,7 +360,7 @@ class Enterprise < ActiveRecord::Base end def ensure_owner_is_manager - users << owner unless users(:reload).include?(owner) || owner.admin? + users << owner unless users.include?(owner) || owner.admin? end def enforce_ownership_limit diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 0bd9b8dfb5..0cd6f08451 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -368,17 +368,18 @@ feature %q{ expect(find("#content-header")).to have_link "New Enterprise" end + end - context "when I have reached my enterprise ownership limit" do - it "does not display the link to create a new enterprise" do - enterprise_user.owned_enterprises.push [supplier1] + context "when I have reached my enterprise ownership limit", js: true do + it "does not display the link to create a new enterprise" do + supplier1.reload + enterprise_user.owned_enterprises.push [supplier1] - click_link "Enterprises" + click_link "Enterprises" - page.should have_content supplier1.name - page.should have_content distributor1.name - expect(find("#content-header")).to_not have_link "New Enterprise" - end + page.should have_content supplier1.name + page.should have_content distributor1.name + expect(find("#content-header")).to_not have_link "New Enterprise" end end From e125bcf45185877e6cb801fb376f19c754d958d7 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 16 Mar 2015 12:15:43 +1100 Subject: [PATCH 214/384] Add column header for extra action column --- app/views/admin/order_cycles/index.html.haml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index a4ae7172a7..e39fdde12a 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -24,6 +24,7 @@ %col %col %col + %col %thead %tr @@ -37,6 +38,7 @@ %th Products %th.actions %th.actions + %th.actions %tbody = f.fields_for :collection do |order_cycle_form| From e38772ada052a134a83f763df281b8aa21f0e5a6 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 16 Mar 2015 12:41:06 +1100 Subject: [PATCH 215/384] Enterprise user can delete unreferenced order cycles --- app/models/spree/ability_decorator.rb | 2 +- spec/models/spree/ability_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 145d8310de..5866a50c3f 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -132,7 +132,7 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization can [:create], OrderCycle - can [:admin, :index, :read, :edit, :update, :bulk_update, :clone], OrderCycle do |order_cycle| + can [:admin, :index, :read, :edit, :update, :bulk_update, :clone, :destroy], OrderCycle do |order_cycle| user.enterprises.include? order_cycle.coordinator end can [:for_order_cycle], Enterprise diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 19006cbcd1..4023961eef 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -371,11 +371,11 @@ module Spree let(:oc2) { create(:simple_order_cycle) } it "should be able to read/write OrderCycles they are the co-ordinator of" do - should have_ability([:admin, :index, :read, :edit, :update, :clone], for: oc1) + should have_ability([:admin, :index, :read, :edit, :update, :clone, :destroy], for: oc1) end it "should not be able to read/write OrderCycles they are not the co-ordinator of" do - should_not have_ability([:admin, :index, :read, :create, :edit, :update, :clone], for: oc2) + should_not have_ability([:admin, :index, :read, :create, :edit, :update, :clone, :destroy], for: oc2) end it "should be able to create OrderCycles" do From 95c09315f50ef324849275d443f6e0a294cba96f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 16 Mar 2015 12:42:05 +1100 Subject: [PATCH 216/384] Change class delete-product -> delete-order-cycle --- app/views/admin/order_cycles/_row.html.haml | 2 +- spec/features/admin/order_cycles_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 659dc91e77..588ebdd745 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -36,4 +36,4 @@ = link_to '', main_app.clone_admin_order_cycle_path(order_cycle), class: 'clone-order-cycle icon-copy no-text' - if can_delete?(order_cycle) %td.actions - = link_to '', main_app.admin_order_cycle_path(order_cycle), class: 'delete-product icon-trash no-text', :method => :delete, data: { confirm: "Are you sure?" } + = link_to '', main_app.admin_order_cycle_path(order_cycle), class: 'delete-order-cycle icon-trash no-text', :method => :delete, data: { confirm: "Are you sure?" } diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 5a4036879f..4bfd0b683c 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -783,7 +783,7 @@ feature %q{ login_to_admin_section click_link 'Order Cycles' page.should have_content("Translusent Berries") - first('a.delete-product').click + first('a.delete-order-cycle').click page.should_not have_content("Translusent Berries") end From a6f0d8f69a0ec574a2bf643dc629f043a31705a5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 16 Mar 2015 12:42:35 +1100 Subject: [PATCH 217/384] Show a nice error message instead of 500 error when deleting a referenced order cycle --- app/controllers/admin/order_cycles_controller.rb | 11 +++++++++++ .../admin/order_cycles_controller_spec.rb | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 435be5b38e..d705cc720c 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -7,6 +7,8 @@ module Admin before_filter :load_data_for_index, :only => :index before_filter :require_coordinator, only: :new + around_filter :protect_invalid_destroy, only: :destroy + def show respond_to do |format| @@ -106,5 +108,14 @@ module Admin render :set_coordinator end end + + def protect_invalid_destroy + begin + yield + rescue ActiveRecord::InvalidForeignKey + redirect_to main_app.admin_order_cycles_url + flash[:error] = "That order cycle has been selected by a customer and cannot be deleted. To prevent customers from accessing it, please close it instead." + end + end end end diff --git a/spec/controllers/admin/order_cycles_controller_spec.rb b/spec/controllers/admin/order_cycles_controller_spec.rb index 598059b5f4..b58884086c 100644 --- a/spec/controllers/admin/order_cycles_controller_spec.rb +++ b/spec/controllers/admin/order_cycles_controller_spec.rb @@ -56,5 +56,21 @@ module Admin end end end + + describe "destroy" do + let!(:distributor) { create(:distributor_enterprise, owner: distributor_owner) } + + describe "when an order cycle becomes non-deletable, and we attempt to delete it" do + let!(:oc) { create(:simple_order_cycle, coordinator: distributor) } + let!(:order) { create(:order, order_cycle: oc) } + + before { spree_get :destroy, id: oc.id } + + it "displays an error message" do + expect(response).to redirect_to admin_order_cycles_path + expect(flash[:error]).to eq "That order cycle has been selected by a customer and cannot be deleted. To prevent customers from accessing it, please close it instead." + end + end + end end end From eb5e411a8ed7f93aad0b9d63a9e909aa145af19d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 16 Mar 2015 14:00:55 +1100 Subject: [PATCH 218/384] Update zeus --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 57e0877ad2..320e680184 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -325,7 +325,7 @@ GEM mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - method_source (0.8.1) + method_source (0.8.2) mime-types (1.25.1) mini_portile (0.6.2) momentjs-rails (2.5.1) @@ -509,7 +509,7 @@ GEM xml-simple (1.1.4) xpath (2.0.0) nokogiri (~> 1.3) - zeus (0.13.3) + zeus (0.15.4) method_source (>= 0.6.7) PLATFORMS From 7af17242f9b26c2f02f7681dc1e9d18f6b800e52 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 16 Mar 2015 14:05:04 +1100 Subject: [PATCH 219/384] EnterpriseFee admin interface allows selecting no fee --- app/views/admin/enterprise_fees/index.html.haml | 4 ++-- spec/features/admin/enterprise_fees_spec.rb | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/views/admin/enterprise_fees/index.html.haml b/app/views/admin/enterprise_fees/index.html.haml index 2237ca8a43..08199d4c4a 100644 --- a/app/views/admin/enterprise_fees/index.html.haml +++ b/app/views/admin/enterprise_fees/index.html.haml @@ -22,10 +22,10 @@ %tr{'ng-repeat' => 'enterprise_fee in enterprise_fees | filter:query'} %td = f.ng_hidden_field :id - = f.ng_collection_select :enterprise_id, @enterprises, :id, :name, 'enterprise_fee.enterprise_id', :include_blank => true + = f.ng_collection_select :enterprise_id, @enterprises, :id, :name, 'enterprise_fee.enterprise_id', include_blank: true %td= f.ng_select :fee_type, enterprise_fee_type_options, 'enterprise_fee.fee_type' %td= f.ng_text_field :name, { placeholder: 'e.g. packing fee' } - %td= f.ng_collection_select :tax_category_id, @tax_categories, :id, :name, 'enterprise_fee.tax_category_id' + %td= f.ng_collection_select :tax_category_id, @tax_categories, :id, :name, 'enterprise_fee.tax_category_id', include_blank: true %td= f.ng_collection_select :calculator_type, @calculators, :name, :description, 'enterprise_fee.calculator_type', {'class' => 'calculator_type', 'ng-model' => 'calculatorType', 'spree-ensure-calculator-preferences-match-type' => "1"} %td{'ng-bind-html-unsafe-compiled' => 'enterprise_fee.calculator_settings'} %td.actions{'spree-delete-resource' => "1"} diff --git a/spec/features/admin/enterprise_fees_spec.rb b/spec/features/admin/enterprise_fees_spec.rb index 36fe1b76ac..fe55d8e90b 100644 --- a/spec/features/admin/enterprise_fees_spec.rb +++ b/spec/features/admin/enterprise_fees_spec.rb @@ -8,7 +8,6 @@ feature %q{ include WebHelper let!(:tax_category_gst) { create(:tax_category, name: 'GST') } - let!(:tax_category_gst_exempt) { create(:tax_category, name: 'GST exempt') } scenario "listing enterprise fees" do fee = create(:enterprise_fee, name: '$0.50 / kg', fee_type: 'packing', tax_category: tax_category_gst) @@ -69,7 +68,7 @@ feature %q{ select 'Foo', from: 'enterprise_fee_set_collection_attributes_0_enterprise_id' select 'Admin', from: 'enterprise_fee_set_collection_attributes_0_fee_type' fill_in 'enterprise_fee_set_collection_attributes_0_name', with: 'Greetings!' - select 'GST exempt', from: 'enterprise_fee_set_collection_attributes_0_tax_category_id' + select '', from: 'enterprise_fee_set_collection_attributes_0_tax_category_id' select 'Flat Percent', from: 'enterprise_fee_set_collection_attributes_0_calculator_type' click_button 'Update' @@ -77,7 +76,7 @@ feature %q{ page.should have_selector "option[selected]", text: 'Foo' page.should have_selector "option[selected]", text: 'Admin' page.should have_selector "input[value='Greetings!']" - page.should have_selector "option[selected]", text: 'GST exempt' + page.should have_select 'enterprise_fee_set_collection_attributes_0_tax_category_id', selected: '' page.should have_selector "option[selected]", text: 'Flat Percent' end From cf4ccc268a8dc73c08789709923a05d8eb68d6b7 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 18 Mar 2015 10:12:32 +1100 Subject: [PATCH 220/384] Admin may set no tax category on product unless tax category is required --- app/views/spree/admin/products/_tax_category_form.html.haml | 2 +- spec/features/admin/products_spec.rb | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/views/spree/admin/products/_tax_category_form.html.haml b/app/views/spree/admin/products/_tax_category_form.html.haml index 81a8677528..3d529e6562 100644 --- a/app/views/spree/admin/products/_tax_category_form.html.haml +++ b/app/views/spree/admin/products/_tax_category_form.html.haml @@ -2,5 +2,5 @@ = f.label :tax_category_id, t(:tax_category) %span.required * %br - = f.collection_select(:tax_category_id, Spree::TaxCategory.all, :id, :name, {:include_blank => Spree::TaxCategory.count > 1}, {:class => "select2 fullwidth"}) + = f.collection_select(:tax_category_id, Spree::TaxCategory.all, :id, :name, {:include_blank => Spree::Config.products_require_tax_category ? false : 'None'}, {:class => "select2 fullwidth"}) = f.error_message_on :tax_category_id diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index 0d1c3b9c6b..ce6b2aba7a 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -118,6 +118,8 @@ feature %q{ end scenario "creating a new product" do + Spree::Config.products_require_tax_category = false + click_link 'Products' click_link 'New Product' @@ -127,7 +129,7 @@ feature %q{ page.should have_selector('#product_supplier_id') select 'Another Supplier', :from => 'product_supplier_id' select taxon.name, from: "product_primary_taxon_id" - select tax_category.name, from: "product_tax_category_id" + select 'None', from: "product_tax_category_id" # Should only have suppliers listed which the user can manage page.should have_select 'product_supplier_id', with_options: [@supplier2.name, @supplier_permitted.name] @@ -138,7 +140,7 @@ feature %q{ flash_message.should == 'Product "A new product !!!" has been successfully created!' product = Spree::Product.find_by_name('A new product !!!') product.supplier.should == @supplier2 - product.tax_category.should == tax_category + product.tax_category.should be_nil end scenario "editing a product" do From ab9bc7b1dcd0c3f0027ecb788ed43f91984cb744 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 18 Mar 2015 12:50:25 +1100 Subject: [PATCH 221/384] Output debugging information to help investigate bogus changes to the sells field through the enterprises bulk edit action --- app/controllers/admin/enterprises_controller.rb | 15 ++++++++++++--- app/views/admin/enterprises/index.html.haml | 4 ++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 37cf4ff09a..94522da36f 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -14,6 +14,7 @@ module Admin helper 'spree/products' + include ActionView::Helpers::TextHelper include OrderCyclesHelper def for_order_cycle @@ -47,12 +48,20 @@ module Admin def bulk_update @enterprise_set = EnterpriseSet.new(collection, params[:enterprise_set]) + touched_enterprises = @enterprise_set.collection.select(&:changed?) if @enterprise_set.save - flash[:success] = 'Enterprises updated successfully' + flash[:success] = "Enterprises updated successfully" + + # 18-3-2015: It seems that the form for this action sometimes loads bogus values for + # the 'sells' field, and submitting that form results in a bunch of enterprises with + # values that have mysteriously changed. This statement is here to help debug that + # issue, and should be removed (along with its display in index.html.haml) when the + # issue has been resolved. + flash[:action] = "Updated #{pluralize(touched_enterprises.count, 'enterprise')}: #{touched_enterprises.map(&:name).join(', ')}" + redirect_to main_app.admin_enterprises_path else - touched_ids = params[:enterprise_set][:collection_attributes].values.map { |v| v[:id].to_i } - @enterprise_set.collection.select! { |e| touched_ids.include? e.id } + @enterprise_set.collection.select! { |e| touched_enterprises.include? e } flash[:error] = 'Update failed' render :index end diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index 64b8423baa..8269cd3699 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -10,6 +10,10 @@ = render :partial => 'spree/shared/error_messages', :locals => { :target => @enterprise_set } +-# For purposes of debugging bulk_update. See Admin/Enterprises#bulk_update. +- if flash[:action] + %p= flash[:action] + = form_for @enterprise_set, url: main_app.bulk_update_admin_enterprises_path do |f| %table#listing_enterprises.index %colgroup From e39d2eb113d14710772f8d6848c29ef1310e706f Mon Sep 17 00:00:00 2001 From: Paul Mackay Date: Mon, 23 Mar 2015 13:47:44 +0000 Subject: [PATCH 222/384] #440: Reset Enterprise columns before querying them. --- db/migrate/20141219034321_add_permalink_to_enterprises.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/db/migrate/20141219034321_add_permalink_to_enterprises.rb b/db/migrate/20141219034321_add_permalink_to_enterprises.rb index 55d8553bf8..5c38ff55a1 100644 --- a/db/migrate/20141219034321_add_permalink_to_enterprises.rb +++ b/db/migrate/20141219034321_add_permalink_to_enterprises.rb @@ -2,6 +2,8 @@ class AddPermalinkToEnterprises < ActiveRecord::Migration def up add_column :enterprises, :permalink, :string + Enterprise.reset_column_information + Enterprise.all.each do |enterprise| counter = 1 permalink = enterprise.name.parameterize From fcfb1aeb87112cdd05932a308f619fab82c58551 Mon Sep 17 00:00:00 2001 From: Paul Mackay Date: Mon, 23 Mar 2015 14:24:16 +0000 Subject: [PATCH 223/384] #440: Try removing validation from update (to prevent geocoding). --- db/migrate/20141219034321_add_permalink_to_enterprises.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20141219034321_add_permalink_to_enterprises.rb b/db/migrate/20141219034321_add_permalink_to_enterprises.rb index 5c38ff55a1..cb0ed0d990 100644 --- a/db/migrate/20141219034321_add_permalink_to_enterprises.rb +++ b/db/migrate/20141219034321_add_permalink_to_enterprises.rb @@ -13,7 +13,7 @@ class AddPermalinkToEnterprises < ActiveRecord::Migration counter += 1 end - enterprise.update_attributes!(permalink: permalink) + enterprise.update_attribute('permalink', permalink) end change_column :enterprises, :permalink, :string, null: false From af9a7a2ed4b8d82d9905cadd750e7ee90664854a Mon Sep 17 00:00:00 2001 From: Paul Mackay Date: Tue, 24 Mar 2015 15:34:45 +0000 Subject: [PATCH 224/384] #443: Support currency configuration. Fix LOCALE variable name. --- config/application.yml.example | 14 ++++++++------ config/initializers/spree.rb | 4 +++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/config/application.yml.example b/config/application.yml.example index 6e11a75dca..f953cb4601 100644 --- a/config/application.yml.example +++ b/config/application.yml.example @@ -1,13 +1,15 @@ # Add application configuration variables here, as shown below. # -# Change this, it has serious security implications. +# Change this, it has serious security implications. # Minimum 30 but usually 128 characters. To obtain run 'rake secret', or faster, 'openssl rand -hex 128' -SECRET_TOKEN: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +SECRET_TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -TIMEZONE: "Melbourne" +TIMEZONE: Melbourne # Default country for dropdowns etc. -DEFAULT_COUNTRY: "Australia" +DEFAULT_COUNTRY: Australia # Locale for translation. -I18N_LOCALE: "en" +LOCALE: en # Spree zone. -CHECKOUT_ZONE: "Australia" +CHECKOUT_ZONE: Australia +# Find currency codes at http://en.wikipedia.org/wiki/ISO_4217. +CURRENCY: AUD diff --git a/config/initializers/spree.rb b/config/initializers/spree.rb index 9c81da88ef..a28763f289 100644 --- a/config/initializers/spree.rb +++ b/config/initializers/spree.rb @@ -11,9 +11,11 @@ require 'spree/product_filters' Spree.config do |config| config.shipping_instructions = true - config.checkout_zone = ENV["CHECKOUT_ZONE"] config.address_requires_state = true + # Settings dependent on locale + config.checkout_zone = ENV["CHECKOUT_ZONE"] + config.currency = ENV['CURRENCY'] if Spree::Country.table_exists? country = Spree::Country.find_by_name(ENV["DEFAULT_COUNTRY"]) config.default_country_id = country.id if country.present? From c6ddf368568af8588e58bf66d441ba437d3a3be2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 11:35:59 +1100 Subject: [PATCH 225/384] Make migration reversible --- ...210233407_add_not_null_to_variant_override_relations.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/db/migrate/20141210233407_add_not_null_to_variant_override_relations.rb b/db/migrate/20141210233407_add_not_null_to_variant_override_relations.rb index e10ab41953..b2f50167c9 100644 --- a/db/migrate/20141210233407_add_not_null_to_variant_override_relations.rb +++ b/db/migrate/20141210233407_add_not_null_to_variant_override_relations.rb @@ -1,6 +1,11 @@ class AddNotNullToVariantOverrideRelations < ActiveRecord::Migration - def change + def up change_column :variant_overrides, :hub_id, :integer, null: false change_column :variant_overrides, :variant_id, :integer, null: false end + + def down + change_column :variant_overrides, :hub_id, :integer, null: true + change_column :variant_overrides, :variant_id, :integer, null: true + end end From c36272ead6a5e016132a3188ea9ed07b6d5188d0 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 11:37:10 +1100 Subject: [PATCH 226/384] Switch to update_column, which will not run geocoding callback of Enterprise --- db/migrate/20141219034321_add_permalink_to_enterprises.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20141219034321_add_permalink_to_enterprises.rb b/db/migrate/20141219034321_add_permalink_to_enterprises.rb index cb0ed0d990..b912180ba7 100644 --- a/db/migrate/20141219034321_add_permalink_to_enterprises.rb +++ b/db/migrate/20141219034321_add_permalink_to_enterprises.rb @@ -13,7 +13,7 @@ class AddPermalinkToEnterprises < ActiveRecord::Migration counter += 1 end - enterprise.update_attribute('permalink', permalink) + enterprise.update_column :permalink, permalink end change_column :enterprises, :permalink, :string, null: false From e2a4b9a8987a9c0209aa248df0e23bdabadfa0ac Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 12:11:06 +1100 Subject: [PATCH 227/384] Fix specs that relied on the old login page which we now redirect to the new page --- spec/features/admin/cms_spec.rb | 6 +++--- spec/support/request/authentication_workflow.rb | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/features/admin/cms_spec.rb b/spec/features/admin/cms_spec.rb index 29fc2412e2..e3dfcf19b9 100644 --- a/spec/features/admin/cms_spec.rb +++ b/spec/features/admin/cms_spec.rb @@ -18,13 +18,13 @@ feature %q{ current_path.should match(/^\/admin/) end - scenario "anonymous user can't access CMS admin" do + scenario "anonymous user can't access CMS admin", js: true do visit cms_admin_path page.should_not have_content "ComfortableMexicanSofa" - page.should have_content "Login" + page.should have_content "Log in" end - scenario "non-admin user can't access CMS admin" do + scenario "non-admin user can't access CMS admin", js: true do login_to_consumer_section visit cms_admin_path page.should_not have_content "ComfortableMexicanSofa" diff --git a/spec/support/request/authentication_workflow.rb b/spec/support/request/authentication_workflow.rb index 0995367600..e71c0dd21a 100644 --- a/spec/support/request/authentication_workflow.rb +++ b/spec/support/request/authentication_workflow.rb @@ -68,9 +68,9 @@ module AuthenticationWorkflow user.spree_roles << user_role visit spree.login_path - fill_in 'spree_user_email', :with => 'someone@ofn.org' - fill_in 'spree_user_password', :with => 'passw0rd' - click_button 'Login' + fill_in 'email', :with => 'someone@ofn.org' + fill_in 'password', :with => 'passw0rd' + click_button 'Log in' end end From 7fb8370c3651f3a88c7a46bd0b8bdd7819532822 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 20 Mar 2015 11:12:56 +1100 Subject: [PATCH 228/384] Look up shipping tax on an order --- app/models/spree/order_decorator.rb | 5 +++++ spec/models/spree/order_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 5d3e317572..ed12d57cbb 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -198,6 +198,11 @@ Spree::Order.class_eval do Spree::ShippingMethod.all_available(self, display_on) end + def shipping_tax + adjustments(:reload).shipping.first.andand.included_tax || 0 + end + + # Overrride of Spree method, that allows us to send separate confirmation emails to user and shop owners def deliver_order_confirmation_email begin diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 3eeac8b40f..d2a0b85a47 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -186,6 +186,27 @@ describe Spree::Order do end end + describe "getting the shipping tax" do + let(:order) { create(:order, shipping_method: shipping_method) } + let(:shipping_method) { create(:shipping_method, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 50.0)) } + + context "with a taxed shipment" do + before do + Spree::Config.shipment_inc_vat = true + Spree::Config.shipping_tax_rate = 0.25 + order.create_shipment! + end + + it "returns the shipping tax" do + order.shipping_tax.should == 10 + end + end + + it "returns zero when the order has not been shipped" do + order.shipping_tax.should == 0 + end + end + describe "setting the distributor" do it "sets the distributor when no order cycle is set" do d = create(:distributor_enterprise) From daa30ed518b6fec6c2f0608c28a4d805217cf9f4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 20 Mar 2015 11:15:49 +1100 Subject: [PATCH 229/384] Retrieve the shipping tax on the order instead of calculating it from scratch --- lib/open_food_network/sales_tax_report.rb | 10 +--------- spec/features/admin/reports_spec.rb | 4 ++++ .../sales_tax_report_spec.rb | 19 ------------------- 3 files changed, 5 insertions(+), 28 deletions(-) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index d5b69011d2..db3660c84b 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -16,7 +16,7 @@ module OpenFoodNetwork @orders.map do |order| totals = totals_of order.line_items shipping_cost = shipping_cost_for order - shipping_tax = shipping_tax_on shipping_cost + shipping_tax = order.shipping_tax [order.number, order.created_at, totals[:items], totals[:items_total], totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, totals[:sales_tax] + shipping_tax, @@ -54,14 +54,6 @@ module OpenFoodNetwork shipping_cost = shipping_cost.nil? ? 0.0 : shipping_cost end - def shipping_tax_on(shipping_cost) - if shipment_inc_vat && shipping_cost.present? - (shipping_cost * shipping_tax_rate / (1 + shipping_tax_rate)).round(2) - else - 0 - end - end - def tax_included_in(line_item) line_item.adjustments.included_tax.sum &:amount end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 28c1366f7a..89fe97357a 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -109,6 +109,8 @@ feature %q{ end describe "Sales tax report" do + let!(:payment_method) { create(:payment_method, distributors: [user1.enterprises.first]) } + let(:user1) do create_enterprise_user(enterprises: [create(:distributor_enterprise)]) end @@ -134,6 +136,8 @@ feature %q{ before do Spree::Config.shipment_inc_vat = true Spree::Config.shipping_tax_rate = 0.2 + + order1.create_shipment! order1.finalize! login_to_admin_as user1 diff --git a/spec/lib/open_food_network/sales_tax_report_spec.rb b/spec/lib/open_food_network/sales_tax_report_spec.rb index 043eac6ae7..00640d8f08 100644 --- a/spec/lib/open_food_network/sales_tax_report_spec.rb +++ b/spec/lib/open_food_network/sales_tax_report_spec.rb @@ -56,24 +56,5 @@ module OpenFoodNetwork end end end - - describe "calculating the shipping tax on a shipping cost" do - it "returns zero when shipping does not include VAT" do - report.stub(:shipment_inc_vat) { false } - report.send(:shipping_tax_on, 12).should == 0 - end - - it "returns zero when no shipping cost is passed" do - report.stub(:shipment_inc_vat) { true } - report.send(:shipping_tax_on, nil).should == 0 - end - - - it "returns the tax included in the price otherwise" do - report.stub(:shipment_inc_vat) { true } - report.stub(:shipping_tax_rate) { 0.2 } - report.send(:shipping_tax_on, 12).should == 2 - end - end end end From d489e06009ff576a34d4ebeb3b965d7256d7da99 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 24 Mar 2015 10:40:54 +1100 Subject: [PATCH 230/384] Retrieve line item sales tax from included_tax rather than tax amount --- lib/open_food_network/sales_tax_report.rb | 2 +- spec/features/admin/reports_spec.rb | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index db3660c84b..a296f13f74 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -55,7 +55,7 @@ module OpenFoodNetwork end def tax_included_in(line_item) - line_item.adjustments.included_tax.sum &:amount + line_item.adjustments.sum &:included_tax end def shipment_inc_vat diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 89fe97357a..724a50fd71 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -110,6 +110,8 @@ feature %q{ describe "Sales tax report" do let!(:payment_method) { create(:payment_method, distributors: [user1.enterprises.first]) } + let!(:zone) { create(:zone, default_tax: true) } + let!(:zone_member) { Spree::ZoneMember.create!(zone: zone, zoneable: Spree::Country.find_by_name('Australia')) } let(:user1) do create_enterprise_user(enterprises: [create(:distributor_enterprise)]) @@ -119,8 +121,8 @@ feature %q{ end let(:tax_category1) { create(:tax_category) } let(:tax_category2) { create(:tax_category) } - let!(:tax_rate1) { create(:tax_rate, amount: 0.0, calculator: Spree::Calculator::DefaultTax.new, tax_category: tax_category1) } - let!(:tax_rate2) { create(:tax_rate, amount: 0.2, calculator: Spree::Calculator::DefaultTax.new, tax_category: tax_category2) } + let!(:tax_rate1) { create(:tax_rate, amount: 0.0, calculator: Spree::Calculator::DefaultTax.new, tax_category: tax_category1, zone: zone) } + let!(:tax_rate2) { create(:tax_rate, amount: 0.2, calculator: Spree::Calculator::DefaultTax.new, tax_category: tax_category2, zone: zone) } let(:product1) { create(:product, price: 12.54, tax_category: tax_category1) } let(:product2) { create(:product, price: 500.15, tax_category: tax_category2) } @@ -137,7 +139,7 @@ feature %q{ Spree::Config.shipment_inc_vat = true Spree::Config.shipping_tax_rate = 0.2 - order1.create_shipment! + 3.times { order1.next } order1.finalize! login_to_admin_as user1 From 69dc92dec165cc15aad163e38733fd61c98e9945 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 24 Mar 2015 11:53:18 +1100 Subject: [PATCH 231/384] Create factories zoned_order and taxed_product --- spec/factories.rb | 24 ++++++++++++++++++++++++ spec/features/admin/reports_spec.rb | 28 ++++++++-------------------- spec/models/spree/adjustment_spec.rb | 4 +--- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/spec/factories.rb b/spec/factories.rb index 7e25534941..4dd30f957a 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -182,6 +182,30 @@ FactoryGirl.define do order.reload end end + + factory :zoned_order, :parent => :order do + after(:create) do |order| + zone = create(:zone, default_tax: true) + Spree::ZoneMember.create!(zone: zone, zoneable: Spree::Country.find_by_name('Australia')) + unless order.bill_address + order.bill_address = create(:address) + order.save! + end + end + end + + factory :taxed_product, :parent => :product do + ignore do + tax_rate_amount 0 + zone nil + end + + tax_category { create(:tax_category) } + + after(:create) do |product, proxy| + create(:tax_rate, amount: proxy.tax_rate_amount, calculator: Spree::Calculator::DefaultTax.new, tax_category: product.tax_category, zone: proxy.zone) + end + end end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 724a50fd71..5561e4f3b2 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -109,31 +109,19 @@ feature %q{ end describe "Sales tax report" do - let!(:payment_method) { create(:payment_method, distributors: [user1.enterprises.first]) } - let!(:zone) { create(:zone, default_tax: true) } - let!(:zone_member) { Spree::ZoneMember.create!(zone: zone, zoneable: Spree::Country.find_by_name('Australia')) } - - let(:user1) do - create_enterprise_user(enterprises: [create(:distributor_enterprise)]) - end - let(:user2) do - create_enterprise_user(enterprises: [create(:distributor_enterprise)]) - end - let(:tax_category1) { create(:tax_category) } - let(:tax_category2) { create(:tax_category) } - let!(:tax_rate1) { create(:tax_rate, amount: 0.0, calculator: Spree::Calculator::DefaultTax.new, tax_category: tax_category1, zone: zone) } - let!(:tax_rate2) { create(:tax_rate, amount: 0.2, calculator: Spree::Calculator::DefaultTax.new, tax_category: tax_category2, zone: zone) } - - let(:product1) { create(:product, price: 12.54, tax_category: tax_category1) } - let(:product2) { create(:product, price: 500.15, tax_category: tax_category2) } - + let(:user1) { create_enterprise_user enterprises: [create(:distributor_enterprise, with_payment_and_shipping: true)] } + let(:user2) { create_enterprise_user enterprises: [create(:distributor_enterprise, with_payment_and_shipping: true)] } let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } - let(:order1) { create(:order, distributor: user1.enterprises.first, shipping_method: shipping_method, bill_address: create(:address)) } + + let(:order1) { create(:zoned_order, distributor: user1.enterprises.first, shipping_method: shipping_method) } + let(:product1) { create(:taxed_product, zone: order1.tax_zone, price: 12.54, tax_rate_amount: 0) } + let(:product2) { create(:taxed_product, zone: order1.tax_zone, price: 500.15, tax_rate_amount: 0.2) } + let!(:line_item1) { create(:line_item, variant: product1.master, price: 12.54, quantity: 1, order: order1) } let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", amount: 100.55) } - let!(:adj_li2_tax) { create(:adjustment, adjustable: line_item2, source: line_item2, originator: tax_rate2, label: "RandomTax", amount: 123.00) } + let!(:adj_li2_tax) { create(:adjustment, adjustable: line_item2, source: line_item2, originator: product2.tax_category.tax_rates.first, label: "RandomTax", amount: 123.00) } before do Spree::Config.shipment_inc_vat = true diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 21475753be..706de139c6 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -7,9 +7,7 @@ module Spree describe "recording included tax" do describe "TaxRate adjustments" do - let!(:zone) { create(:zone, default_tax: true) } - let!(:zone_member) { ZoneMember.create!(zone: zone, zoneable: Country.find_by_name('Australia')) } - let!(:order) { create(:order) } + let!(:order) { create(:zoned_order) } let!(:line_item) { create(:line_item, order: order) } let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::FlatRate.new(preferred_amount: 0.1)) } let(:adjustment) { line_item.adjustments(:reload).first } From 96506926996ee9f1ee1f20295bef6ae3c4c7f1aa Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 14:27:42 +1100 Subject: [PATCH 232/384] Calculate total tax on enterprise fees for an order --- app/models/spree/order_decorator.rb | 4 ++++ spec/models/spree/order_spec.rb | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index ed12d57cbb..11884dfb92 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -202,6 +202,10 @@ Spree::Order.class_eval do adjustments(:reload).shipping.first.andand.included_tax || 0 end + def enterprise_fee_tax + adjustments(:reload).enterprise_fee.sum(&:included_tax) + end + # Overrride of Spree method, that allows us to send separate confirmation emails to user and shop owners def deliver_order_confirmation_email diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index d2a0b85a47..6a0be4a66d 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -207,6 +207,18 @@ describe Spree::Order do end end + describe "getting the enterprise fee tax" do + let!(:order) { create(:order) } + let(:enterprise_fee1) { create(:enterprise_fee) } + let(:enterprise_fee2) { create(:enterprise_fee) } + let!(:adjustment1) { create(:adjustment, adjustable: order, originator: enterprise_fee1, label: "EF 1", amount: 123, included_tax: 10.00) } + let!(:adjustment2) { create(:adjustment, adjustable: order, originator: enterprise_fee2, label: "EF 2", amount: 123, included_tax: 2.00) } + + it "returns a sum of the tax included in all enterprise fees" do + order.reload.enterprise_fee_tax.should == 12 + end + end + describe "setting the distributor" do it "sets the distributor when no order cycle is set" do d = create(:distributor_enterprise) From 40b4edeca1046b1807ccc1c4a8e0dda348e10f84 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 14:50:26 +1100 Subject: [PATCH 233/384] Replace zoned_order factory with zone_with_member. In practice, former had issues with circular dependencies. --- spec/factories.rb | 11 ++++------- spec/features/admin/reports_spec.rb | 7 ++++--- spec/models/spree/adjustment_spec.rb | 6 +++--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/spec/factories.rb b/spec/factories.rb index 4dd30f957a..3a1738de2c 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -183,14 +183,11 @@ FactoryGirl.define do end end - factory :zoned_order, :parent => :order do - after(:create) do |order| - zone = create(:zone, default_tax: true) + factory :zone_with_member, :parent => :zone do + default_tax true + + after(:create) do |zone| Spree::ZoneMember.create!(zone: zone, zoneable: Spree::Country.find_by_name('Australia')) - unless order.bill_address - order.bill_address = create(:address) - order.save! - end end end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 5561e4f3b2..935a44aa71 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -113,9 +113,10 @@ feature %q{ let(:user2) { create_enterprise_user enterprises: [create(:distributor_enterprise, with_payment_and_shipping: true)] } let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } - let(:order1) { create(:zoned_order, distributor: user1.enterprises.first, shipping_method: shipping_method) } - let(:product1) { create(:taxed_product, zone: order1.tax_zone, price: 12.54, tax_rate_amount: 0) } - let(:product2) { create(:taxed_product, zone: order1.tax_zone, price: 500.15, tax_rate_amount: 0.2) } + let!(:zone) { create(:zone_with_member) } + let(:order1) { create(:order, distributor: user1.enterprises.first, shipping_method: shipping_method, bill_address: create(:address)) } + let(:product1) { create(:taxed_product, zone: zone, price: 12.54, tax_rate_amount: 0) } + let(:product2) { create(:taxed_product, zone: zone, price: 500.15, tax_rate_amount: 0.2) } let!(:line_item1) { create(:line_item, variant: product1.master, price: 12.54, quantity: 1, order: order1) } let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 706de139c6..e69d04da6f 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -7,7 +7,8 @@ module Spree describe "recording included tax" do describe "TaxRate adjustments" do - let!(:order) { create(:zoned_order) } + let!(:zone) { create(:zone_with_member) } + let!(:order) { create(:order, bill_address: create(:address)) } let!(:line_item) { create(:line_item, order: order) } let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::FlatRate.new(preferred_amount: 0.1)) } let(:adjustment) { line_item.adjustments(:reload).first } @@ -83,8 +84,7 @@ module Spree end describe "EnterpriseFee adjustments" do - let!(:zone) { create(:zone, default_tax: true) } - let!(:zone_member) { ZoneMember.create!(zone: zone, zoneable: Country.find_by_name('Australia')) } + let!(:zone) { create(:zone_with_member) } let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::DefaultTax.new, zone: zone, amount: 0.1) } let(:tax_category) { create(:tax_category, tax_rates: [tax_rate]) } From 333a4ecf2fd5775040c2b82e54020df32272f3de Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 15:30:36 +1100 Subject: [PATCH 234/384] Include enterprise fee tax on sales tax report --- lib/open_food_network/sales_tax_report.rb | 7 ++++--- spec/features/admin/reports_spec.rb | 24 ++++++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index a296f13f74..668e5ee04e 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -8,7 +8,7 @@ module OpenFoodNetwork def header ["Order number", "Date", "Items", "Items total (#{currency_symbol})", "Taxable Items Total (#{currency_symbol})", - "Sales Tax (#{currency_symbol})", "Delivery Charge (#{currency_symbol})", "Tax on Delivery (#{currency_symbol})", + "Sales Tax (#{currency_symbol})", "Delivery Charge (#{currency_symbol})", "Tax on Delivery (#{currency_symbol})", "Tax on Fees (#{currency_symbol})", "Total Tax (#{currency_symbol})", "Customer", "Distributor"] end @@ -17,9 +17,10 @@ module OpenFoodNetwork totals = totals_of order.line_items shipping_cost = shipping_cost_for order shipping_tax = order.shipping_tax - + enterprise_fee_tax = order.enterprise_fee_tax + [order.number, order.created_at, totals[:items], totals[:items_total], - totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, totals[:sales_tax] + shipping_tax, + totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, enterprise_fee_tax, totals[:sales_tax] + shipping_tax + enterprise_fee_tax, order.bill_address.full_name, order.distributor.andand.name] end end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 935a44aa71..bb64c52d2c 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -109,12 +109,16 @@ feature %q{ end describe "Sales tax report" do - let(:user1) { create_enterprise_user enterprises: [create(:distributor_enterprise, with_payment_and_shipping: true)] } - let(:user2) { create_enterprise_user enterprises: [create(:distributor_enterprise, with_payment_and_shipping: true)] } + let(:distributor1) { create(:distributor_enterprise, with_payment_and_shipping: true) } + let(:distributor2) { create(:distributor_enterprise, with_payment_and_shipping: true) } + let(:user1) { create_enterprise_user enterprises: [distributor1] } + let(:user2) { create_enterprise_user enterprises: [distributor2] } let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } + let(:enterprise_fee) { create(:enterprise_fee, enterprise: user1.enterprises.first, tax_category: product2.tax_category, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 120.0)) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: distributor1, coordinator_fees: [enterprise_fee], distributors: [distributor1], variants: [product1.master]) } let!(:zone) { create(:zone_with_member) } - let(:order1) { create(:order, distributor: user1.enterprises.first, shipping_method: shipping_method, bill_address: create(:address)) } + let(:order1) { create(:order, order_cycle: order_cycle, distributor: user1.enterprises.first, shipping_method: shipping_method, bill_address: create(:address)) } let(:product1) { create(:taxed_product, zone: zone, price: 12.54, tax_rate_amount: 0) } let(:product2) { create(:taxed_product, zone: zone, price: 500.15, tax_rate_amount: 0.2) } @@ -129,6 +133,8 @@ feature %q{ Spree::Config.shipping_tax_rate = 0.2 3.times { order1.next } + order1.reload.update_distribution_charge! + order1.finalize! login_to_admin_as user1 @@ -149,14 +155,18 @@ feature %q{ page.should have_content "#{order1.number}" # And the totals and sales tax should be correct - page.should have_content "1512.99" # items total - page.should have_content "1500.45" # taxable items total - page.should have_content "123.0" # sales tax (from adj_li2_tax, not calculated on the fly) + page.should have_content "1512.99" # items total + page.should have_content "1500.45" # taxable items total + page.should have_content "123.0" # sales tax (from adj_li2_tax, not calculated on the fly) page.should_not have_content "250.08" # the number that would have been calculated on the fly + page.should have_content "20.0" # enterprise fee tax # And the shipping cost and tax should be correct page.should have_content "100.55" # shipping cost - page.should have_content "16.76" # shipping tax # TODO: do not calculate on the fly + page.should have_content "16.76" # shipping tax + + # And the total tax should be correct + page.should have_content "159.76" # total tax end end From 84f30972178eb25b7077b087c61330b80f213bc8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 16:07:03 +1100 Subject: [PATCH 235/384] Calculate total tax from the tax included in all the adjustments on the order --- lib/open_food_network/sales_tax_report.rb | 3 ++- spec/factories.rb | 2 +- spec/features/admin/reports_spec.rb | 6 ++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 668e5ee04e..be48b04a02 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -18,9 +18,10 @@ module OpenFoodNetwork shipping_cost = shipping_cost_for order shipping_tax = order.shipping_tax enterprise_fee_tax = order.enterprise_fee_tax + total_tax = (order.adjustments + order.price_adjustments).sum(&:included_tax) [order.number, order.created_at, totals[:items], totals[:items_total], - totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, enterprise_fee_tax, totals[:sales_tax] + shipping_tax + enterprise_fee_tax, + totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, enterprise_fee_tax, total_tax, order.bill_address.full_name, order.distributor.andand.name] end end diff --git a/spec/factories.rb b/spec/factories.rb index 3a1738de2c..28ea617bd2 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -200,7 +200,7 @@ FactoryGirl.define do tax_category { create(:tax_category) } after(:create) do |product, proxy| - create(:tax_rate, amount: proxy.tax_rate_amount, calculator: Spree::Calculator::DefaultTax.new, tax_category: product.tax_category, zone: proxy.zone) + create(:tax_rate, amount: proxy.tax_rate_amount, tax_category: product.tax_category, included_in_price: true, calculator: Spree::Calculator::DefaultTax.new, zone: proxy.zone) end end end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index bb64c52d2c..f16b736c00 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -126,7 +126,6 @@ feature %q{ let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", amount: 100.55) } - let!(:adj_li2_tax) { create(:adjustment, adjustable: line_item2, source: line_item2, originator: product2.tax_category.tax_rates.first, label: "RandomTax", amount: 123.00) } before do Spree::Config.shipment_inc_vat = true @@ -157,8 +156,7 @@ feature %q{ # And the totals and sales tax should be correct page.should have_content "1512.99" # items total page.should have_content "1500.45" # taxable items total - page.should have_content "123.0" # sales tax (from adj_li2_tax, not calculated on the fly) - page.should_not have_content "250.08" # the number that would have been calculated on the fly + page.should have_content "250.08" # sales tax page.should have_content "20.0" # enterprise fee tax # And the shipping cost and tax should be correct @@ -166,7 +164,7 @@ feature %q{ page.should have_content "16.76" # shipping tax # And the total tax should be correct - page.should have_content "159.76" # total tax + page.should have_content "286.84" # total tax end end From 89d4a59e9d31efb0d9937cdf58c26651de10de0d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 16:14:46 +1100 Subject: [PATCH 236/384] Extract order total tax calculations to model --- app/models/spree/order_decorator.rb | 3 +++ lib/open_food_network/sales_tax_report.rb | 2 +- spec/models/spree/order_spec.rb | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 11884dfb92..eab5b00d72 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -206,6 +206,9 @@ Spree::Order.class_eval do adjustments(:reload).enterprise_fee.sum(&:included_tax) end + def total_tax + (adjustments + price_adjustments).sum &:included_tax + end # Overrride of Spree method, that allows us to send separate confirmation emails to user and shop owners def deliver_order_confirmation_email diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index be48b04a02..d6d58d8c15 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -18,7 +18,7 @@ module OpenFoodNetwork shipping_cost = shipping_cost_for order shipping_tax = order.shipping_tax enterprise_fee_tax = order.enterprise_fee_tax - total_tax = (order.adjustments + order.price_adjustments).sum(&:included_tax) + total_tax = order.total_tax [order.number, order.created_at, totals[:items], totals[:items_total], totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, enterprise_fee_tax, total_tax, diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 6a0be4a66d..df5850f823 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -219,6 +219,24 @@ describe Spree::Order do end end + describe "getting the total tax" do + let(:order) { create(:order, shipping_method: shipping_method) } + let(:shipping_method) { create(:shipping_method, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 50.0)) } + let(:enterprise_fee) { create(:enterprise_fee) } + let!(:adjustment) { create(:adjustment, adjustable: order, originator: enterprise_fee, label: "EF", amount: 123, included_tax: 2) } + + before do + Spree::Config.shipment_inc_vat = true + Spree::Config.shipping_tax_rate = 0.25 + order.create_shipment! + order.reload + end + + it "returns a sum of all tax on the order" do + order.total_tax.should == 12 + end + end + describe "setting the distributor" do it "sets the distributor when no order cycle is set" do d = create(:distributor_enterprise) From d8656a36c67467db42700b9f7935c10693e0bf85 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 25 Mar 2015 16:31:15 +1100 Subject: [PATCH 237/384] Consistency in tax calculations, remove some unneeded local vars --- app/models/spree/order_decorator.rb | 4 ++-- lib/open_food_network/sales_tax_report.rb | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index eab5b00d72..c29d3f8165 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -199,11 +199,11 @@ Spree::Order.class_eval do end def shipping_tax - adjustments(:reload).shipping.first.andand.included_tax || 0 + adjustments(:reload).shipping.sum &:included_tax end def enterprise_fee_tax - adjustments(:reload).enterprise_fee.sum(&:included_tax) + adjustments(:reload).enterprise_fee.sum &:included_tax end def total_tax diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index d6d58d8c15..46e2cd234e 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -16,12 +16,9 @@ module OpenFoodNetwork @orders.map do |order| totals = totals_of order.line_items shipping_cost = shipping_cost_for order - shipping_tax = order.shipping_tax - enterprise_fee_tax = order.enterprise_fee_tax - total_tax = order.total_tax [order.number, order.created_at, totals[:items], totals[:items_total], - totals[:taxable_total], totals[:sales_tax], shipping_cost, shipping_tax, enterprise_fee_tax, total_tax, + totals[:taxable_total], totals[:sales_tax], shipping_cost, order.shipping_tax, order.enterprise_fee_tax, order.total_tax, order.bill_address.full_name, order.distributor.andand.name] end end From 09a102bd8b410f19167b1c641f48bebe82ec790f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 27 Mar 2015 11:53:13 +1100 Subject: [PATCH 238/384] When an enterprise user with a hub creates a new producer, it does not default to sells any #453 --- app/controllers/admin/enterprises_controller.rb | 3 ++- .../controllers/admin/enterprises_controller_spec.rb | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 94522da36f..d2b25c55df 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -133,7 +133,8 @@ module Admin def override_sells unless spree_current_user.admin? has_hub = spree_current_user.enterprises.is_hub.any? - params[:enterprise][:sells] = has_hub ? 'any' : 'none' + new_enterprise_is_producer = !!params[:enterprise][:is_primary_producer] + params[:enterprise][:sells] = (has_hub && !new_enterprise_is_producer) ? 'any' : 'none' end end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 86b128af38..6f108da003 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -48,7 +48,7 @@ module Admin end context "when I already have a hub" do - it "creates the new enterprise as a hub" do + it "creates new non-producers as hubs" do controller.stub spree_current_user: distributor_manager enterprise_params[:enterprise][:owner_id] = distributor_manager @@ -57,6 +57,16 @@ module Admin enterprise.sells.should == 'any' end + it "creates new producers as sells none" do + controller.stub spree_current_user: distributor_manager + enterprise_params[:enterprise][:owner_id] = distributor_manager + enterprise_params[:enterprise][:is_primary_producer] = '1' + + spree_put :create, enterprise_params + enterprise = Enterprise.find_by_name 'zzz' + enterprise.sells.should == 'none' + end + it "doesn't affect the hub status for super admins" do admin_user.enterprises << create(:distributor_enterprise) From d4713684120e29dfbefb957261cfdc3ca464fe72 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 27 Mar 2015 13:09:25 +1100 Subject: [PATCH 239/384] Spacing --- .../registration/registration_controller.js.coffee | 2 +- app/views/registration/index.html.haml | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee index 3be0378619..29a60c3f15 100644 --- a/app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee @@ -3,7 +3,7 @@ Darkswarm.controller "RegistrationCtrl", ($scope, RegistrationService, Enterpris $scope.enterprise = EnterpriseRegistrationService.enterprise $scope.select = RegistrationService.select - $scope.steps = ['details','contact','type','about','images','social'] + $scope.steps = ['details', 'contact', 'type', 'about', 'images', 'social'] $scope.countries = availableCountries diff --git a/app/views/registration/index.html.haml b/app/views/registration/index.html.haml index de09d9494f..d110e98657 100644 --- a/app/views/registration/index.html.haml +++ b/app/views/registration/index.html.haml @@ -1,4 +1,5 @@ -=inject_spree_api_key -=inject_available_countries -=inject_enterprise_attributes -%div{ "ng-controller" => "RegistrationCtrl" } \ No newline at end of file += inject_spree_api_key += inject_available_countries += inject_enterprise_attributes + +%div{ "ng-controller" => "RegistrationCtrl" } From 0ed1eeffd6c68d9345eb1d1a32fcd8ec9e4e55ce Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 27 Mar 2015 14:05:58 +1100 Subject: [PATCH 240/384] When registering a new enterprise, if user is signed in, owns a hub and is not making a producer, the new enterprise becomes a hub --- app/controllers/api/enterprises_controller.rb | 4 +++- spec/controllers/api/enterprises_controller_spec.rb | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/enterprises_controller.rb b/app/controllers/api/enterprises_controller.rb index d40d67409d..e51d21ef3c 100644 --- a/app/controllers/api/enterprises_controller.rb +++ b/app/controllers/api/enterprises_controller.rb @@ -63,7 +63,9 @@ module Api end def override_sells - params[:enterprise][:sells] = 'unspecified' + has_hub = current_api_user.enterprises.is_hub.any? + new_enterprise_is_producer = !!params[:enterprise][:is_primary_producer] + params[:enterprise][:sells] = (has_hub && !new_enterprise_is_producer) ? 'any' : 'unspecified' end def override_visible diff --git a/spec/controllers/api/enterprises_controller_spec.rb b/spec/controllers/api/enterprises_controller_spec.rb index e21a879d4f..ae6b02fac7 100644 --- a/spec/controllers/api/enterprises_controller_spec.rb +++ b/spec/controllers/api/enterprises_controller_spec.rb @@ -20,6 +20,19 @@ module Api Spree.user_class.stub :find_by_spree_api_key => enterprise_manager end + describe "creating an enterprise" do + let(:australia) { Spree::Country.find_by_name('Australia') } + let(:new_enterprise_params) { {enterprise: {name: 'name', email: 'email@example.com', address_attributes: {address1: '123 Abc Street', city: 'Northcote', zipcode: '3070', state_id: australia.states.first, country_id: australia.id } } } } + + it "creates as sells=any when it is not a producer" do + spree_post :create, new_enterprise_params + response.should be_success + + enterprise = Enterprise.last + enterprise.sells.should == 'any' + end + end + describe "submitting a valid image" do before do enterprise.stub(:update_attributes).and_return(true) From 803781173583d9c5716c390eb106e9c5acfe531d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 27 Mar 2015 14:38:21 +1100 Subject: [PATCH 241/384] Only make new enterprises as hubs when current user *owns*, not just manages a hub. --- app/controllers/admin/enterprises_controller.rb | 2 +- app/controllers/api/enterprises_controller.rb | 2 +- .../admin/enterprises_controller_spec.rb | 14 ++++++++------ .../api/enterprises_controller_spec.rb | 17 +++++++++++++---- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index d2b25c55df..e48ef339b3 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -132,7 +132,7 @@ module Admin def override_sells unless spree_current_user.admin? - has_hub = spree_current_user.enterprises.is_hub.any? + has_hub = spree_current_user.owned_enterprises.is_hub.any? new_enterprise_is_producer = !!params[:enterprise][:is_primary_producer] params[:enterprise][:sells] = (has_hub && !new_enterprise_is_producer) ? 'any' : 'none' end diff --git a/app/controllers/api/enterprises_controller.rb b/app/controllers/api/enterprises_controller.rb index e51d21ef3c..468f3a22ad 100644 --- a/app/controllers/api/enterprises_controller.rb +++ b/app/controllers/api/enterprises_controller.rb @@ -63,7 +63,7 @@ module Api end def override_sells - has_hub = current_api_user.enterprises.is_hub.any? + has_hub = current_api_user.owned_enterprises.is_hub.any? new_enterprise_is_producer = !!params[:enterprise][:is_primary_producer] params[:enterprise][:sells] = (has_hub && !new_enterprise_is_producer) ? 'any' : 'unspecified' end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 6f108da003..2262d508ef 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -8,7 +8,7 @@ module Admin let(:admin_user) { create(:admin_user) } let(:distributor_manager) { create(:user, enterprise_limit: 10, enterprises: [distributor]) } let(:supplier_manager) { create(:user, enterprise_limit: 10, enterprises: [supplier]) } - let(:distributor_owner) { create(:user) } + let(:distributor_owner) { create(:user, enterprise_limit: 10) } let(:supplier_owner) { create(:user) } let(:distributor) { create(:distributor_enterprise, owner: distributor_owner ) } @@ -47,10 +47,12 @@ module Admin distributor_manager.enterprise_roles.where(enterprise_id: enterprise).first.should be end - context "when I already have a hub" do + context "when I already own a hub" do + before { distributor } + it "creates new non-producers as hubs" do - controller.stub spree_current_user: distributor_manager - enterprise_params[:enterprise][:owner_id] = distributor_manager + controller.stub spree_current_user: distributor_owner + enterprise_params[:enterprise][:owner_id] = distributor_owner spree_put :create, enterprise_params enterprise = Enterprise.find_by_name 'zzz' @@ -58,8 +60,8 @@ module Admin end it "creates new producers as sells none" do - controller.stub spree_current_user: distributor_manager - enterprise_params[:enterprise][:owner_id] = distributor_manager + controller.stub spree_current_user: distributor_owner + enterprise_params[:enterprise][:owner_id] = distributor_owner enterprise_params[:enterprise][:is_primary_producer] = '1' spree_put :create, enterprise_params diff --git a/spec/controllers/api/enterprises_controller_spec.rb b/spec/controllers/api/enterprises_controller_spec.rb index ae6b02fac7..77e16368d9 100644 --- a/spec/controllers/api/enterprises_controller_spec.rb +++ b/spec/controllers/api/enterprises_controller_spec.rb @@ -12,12 +12,12 @@ module Api Enterprise.stub(:find).and_return(enterprise) end - describe "as an enterprise manager" do - let(:enterprise_manager) { create_enterprise_user } + context "as an enterprise owner" do + let(:enterprise_owner) { create_enterprise_user enterprise_limit: 10 } + let(:enterprise) { create(:distributor_enterprise, owner: enterprise_owner) } before do - enterprise_manager.enterprise_roles.build(enterprise: enterprise).save - Spree.user_class.stub :find_by_spree_api_key => enterprise_manager + Spree.user_class.stub :find_by_spree_api_key => enterprise_owner end describe "creating an enterprise" do @@ -32,6 +32,15 @@ module Api enterprise.sells.should == 'any' end end + end + + context "as an enterprise manager" do + let(:enterprise_manager) { create_enterprise_user } + + before do + enterprise_manager.enterprise_roles.build(enterprise: enterprise).save + Spree.user_class.stub :find_by_spree_api_key => enterprise_manager + end describe "submitting a valid image" do before do From 0b082c964b512d1be3594cdf9640f56c99726af4 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 4 Mar 2015 09:47:48 +1100 Subject: [PATCH 242/384] Restricting which enterprises can coordinate an order cycle --- app/helpers/order_cycles_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index fb523f2c98..8dc887e99e 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -16,7 +16,7 @@ module OrderCyclesHelper end def order_cycle_coordinating_enterprises - order_cycle_permitted_enterprises.is_distributor.by_name + Enterprise.managed_by(spree_current_user).is_distributor.by_name end def order_cycle_coordinating_enterprise_options From a9c7e4b716f877621095b9bdaece9a395b5a59e6 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Feb 2015 10:13:04 +1100 Subject: [PATCH 243/384] Order cycles index shows all OrderCycles I am involved in --- app/controllers/admin/order_cycles_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index d705cc720c..f7c623c6eb 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -76,7 +76,7 @@ module Admin protected def collection(show_more=false) - ocs = OrderCycle.managed_by(spree_current_user) + ocs = OrderCycle.accessible_by(spree_current_user) ocs.undated + ocs.soonest_closing + From 75ed25955432f2d31e1b50488a49430bc9bd4e28 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Feb 2015 10:22:05 +1100 Subject: [PATCH 244/384] Conditionally show input for coordinator Conflicts: app/views/admin/order_cycles/_form.html.haml --- app/views/admin/order_cycles/_form.html.haml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index b624bbd4b2..89cd149bd2 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -21,10 +21,12 @@ %h2 Coordinator -= f.label :coordinator_id, 'Coordinator' -= f.select :coordinator_id, order_cycle_coordinating_enterprise_options, { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} -= render 'coordinator_fees', f: f - +-if current_user.enterprises.include? @order_cycle.coordinator + = f.label :coordinator_id, 'Coordinator' + = f.select :coordinator_id, order_cycle_coordinating_enterprise_options, { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} + = render 'coordinator_fees', f: f +- else + = @order_cycle.coordinator.name %h2 Outgoing %table.exchanges From f8a965233317d2365b04a882a8ecac6a3d154bde Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 12 Mar 2015 12:24:18 +1100 Subject: [PATCH 245/384] Moving SASS comments to thier own lines --- .../darkswarm/active_table.css.sass | 32 ++++++++----------- app/assets/stylesheets/darkswarm/map.css.sass | 5 +-- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/active_table.css.sass b/app/assets/stylesheets/darkswarm/active_table.css.sass index 27b98850b7..b8280d22ca 100644 --- a/app/assets/stylesheets/darkswarm/active_table.css.sass +++ b/app/assets/stylesheets/darkswarm/active_table.css.sass @@ -30,20 +30,20 @@ text-decoration: underline span.margin-top - margin-top: 0.5rem + margin-top: 0.5rem display: inline-block // Generic text resize - @media all and (max-width: 640px) + @media all and (max-width: 640px) &, & * - font-size: 0.875rem + font-size: 0.875rem fat > div label - &, & * - font-size: 0.75rem + &, & * + font-size: 0.75rem - - .active_table_row // Inherits from active_table - border: 1px solid transparent + // Inherits from active_table + .active_table_row + border: 1px solid transparent @include border-radius(0.5em) // Foundation overrides @@ -76,15 +76,15 @@ .active_table_row:last-child border-bottom: 1px solid $disabled-bright @include border-radius-mixed(0, 0, 0.5em, 0.5em) - - + + //Open row sections .fat > div border-top: 1px solid #aaa - @media all and (max-width: 640px) + @media all and (max-width: 640px) margin-top: 1em - ul, ol + ul, ol font-size: 0.875rem [class*="block-grid-"] > li @@ -96,10 +96,10 @@ margin-top: 0.25rem margin-bottom: 0.25rem color: #777 - + p.trans-sentence text-transform: capitalize - + &.closed &:hover, &:active, &:focus .active_table_row.closed @@ -112,7 +112,3 @@ &.open .active_table_row:first-child color: $dark-grey - - - - diff --git a/app/assets/stylesheets/darkswarm/map.css.sass b/app/assets/stylesheets/darkswarm/map.css.sass index 53e8fb5768..a831e9448c 100644 --- a/app/assets/stylesheets/darkswarm/map.css.sass +++ b/app/assets/stylesheets/darkswarm/map.css.sass @@ -10,11 +10,12 @@ height: 100% width: 100% - img // https://github.com/zurb/foundation/issues/112 + // https://github.com/zurb/foundation/issues/112 + img max-width: none height: auto - #pac-input + #pac-input @include big-input(#888, #333, $clr-brick) @include big-input-static font-size: 1.5rem From 75a37e16e3a56c13abca130062dc2181bad0cbde Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 13 Mar 2015 14:29:21 +1100 Subject: [PATCH 246/384] Remove elements of order cycle form that coordinators cannot edit --- app/views/admin/order_cycles/_form.html.haml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 89cd149bd2..387b2627ec 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -15,13 +15,14 @@ %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_supplied_products_form' -= select_tag :new_supplier_id, options_for_select(order_cycle_producer_enterprise_options), {'ng-model' => 'new_supplier_id'} +- if spree_current_user.enterprises.include? @order_cycle.coordinator + = select_tag :new_supplier_id, options_for_select(order_cycle_producer_enterprise_options), {'ng-model' => 'new_supplier_id'} -= f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' + = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' %h2 Coordinator --if current_user.enterprises.include? @order_cycle.coordinator +-if spree_current_user.enterprises.include? @order_cycle.coordinator = f.label :coordinator_id, 'Coordinator' = f.select :coordinator_id, order_cycle_coordinating_enterprise_options, { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} = render 'coordinator_fees', f: f @@ -43,8 +44,9 @@ %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_distributed_products_form' -= select_tag :new_distributor_id, options_for_select(order_cycle_hub_enterprise_options), {'ng-model' => 'new_distributor_id'} -= f.submit 'Add distributor', 'ng-click' => 'addDistributor($event)' +- if spree_current_user.enterprises.include? @order_cycle.coordinator + = select_tag :new_distributor_id, options_for_select(order_cycle_hub_enterprise_options), {'ng-model' => 'new_distributor_id'} + = f.submit 'Add distributor', 'ng-click' => 'addDistributor($event)' .actions = f.submit @order_cycle.new_record? ? 'Create' : 'Update', 'ng-disabled' => '!loaded()' From 85e4b3970c06b3f6fefa645d674e6fbba9cdebc0 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 13 Mar 2015 14:29:55 +1100 Subject: [PATCH 247/384] Adding an 'involved' scope to exchanges --- app/models/exchange.rb | 1 + spec/models/exchange_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/app/models/exchange.rb b/app/models/exchange.rb index c491748d5d..1b226d269a 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -22,6 +22,7 @@ class Exchange < ActiveRecord::Base scope :to_enterprise, lambda { |enterprise| where(receiver_id: enterprise) } scope :from_enterprises, lambda { |enterprises| where('exchanges.sender_id IN (?)', enterprises) } scope :to_enterprises, lambda { |enterprises| where('exchanges.receiver_id IN (?)', enterprises) } + scope :involving, lambda { |enterprises| where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises).select('DISTINCT exchanges.*') } scope :supplying_to, lambda { |distributor| where('exchanges.incoming OR exchanges.receiver_id = ?', distributor) } scope :with_variant, lambda { |variant| joins(:exchange_variants).where('exchange_variants.variant_id = ?', variant) } scope :with_any_variant, lambda { |variants| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', variants).select('DISTINCT exchanges.*') } diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index 0dfceadbad..82f360aa16 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -177,6 +177,12 @@ describe Exchange do Exchange.to_enterprises([coordinator]).should == [incoming_exchange] Exchange.to_enterprises([coordinator, distributor]).sort.should == [incoming_exchange, outgoing_exchange].sort end + + it "finds exchanges involving any of a number of enterprises" do + Exchange.involving([supplier]).should == [incoming_exchange] + Exchange.involving([coordinator]).sort.should == [incoming_exchange, outgoing_exchange].sort + Exchange.involving([distributor]).should == [outgoing_exchange] + end end describe "finding exchanges supplying to a distributor" do From 2310a6a7db073d3023d3e7c090bd4ab73f28330b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 13 Mar 2015 15:30:24 +1100 Subject: [PATCH 248/384] Splitting out order cycle abilities from general order management abilities --- app/models/spree/ability_decorator.rb | 22 ++++++++-- spec/models/spree/ability_spec.rb | 62 ++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 5866a50c3f..5953d56ffd 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -8,6 +8,7 @@ class AbilityDecorator add_enterprise_management_abilities user if can_manage_enterprises? user add_group_management_abilities user if can_manage_groups? user add_product_management_abilities user if can_manage_products? user + add_order_cycle_management_abilities user if can_manage_order_cycles? user add_order_management_abilities user if can_manage_orders? user add_relationship_management_abilities user if can_manage_relationships? user end @@ -33,6 +34,13 @@ class AbilityDecorator user.enterprises.any? { |e| e.category != :hub_profile && e.producer_profile_only != true } end + # Users can manage order cycles if they manage a sells own/any enterprise + # OR if they manage a producer which is included in any order cycles + def can_manage_order_cycles?(user) + can_manage_orders?(user) || + OrderCycle.accessible_by(user).any? + end + # Users can manage orders if they have a sells own/any enterprise. def can_manage_orders?(user) ( user.enterprises.map(&:sells) & %w(own any) ).any? @@ -115,6 +123,16 @@ class AbilityDecorator can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory], :report end + def add_order_cycle_management_abilities(user) + can [:admin, :index, :read, :edit, :update], OrderCycle do |order_cycle| + OrderCycle.accessible_by(user).include? order_cycle + end + can [:bulk_update, :clone, :destroy], OrderCycle do |order_cycle| + user.enterprises.include? order_cycle.coordinator + end + can [:for_order_cycle], Enterprise + end + def add_order_management_abilities(user) # Enterprise User can only access orders that they are a distributor for can [:index, :create], Spree::Order @@ -132,10 +150,6 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization can [:create], OrderCycle - can [:admin, :index, :read, :edit, :update, :bulk_update, :clone, :destroy], OrderCycle do |order_cycle| - user.enterprises.include? order_cycle.coordinator - end - can [:for_order_cycle], Enterprise can [:admin, :index, :read, :create, :edit, :update], ExchangeVariant can [:admin, :index, :read, :create, :edit, :update], Exchange diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 4023961eef..bb45487894 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -24,6 +24,7 @@ module Spree 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 } + it { subject.can_manage_order_cycles?(user).should be_true } end context "as manager of an enterprise who sell 'own'" do @@ -34,6 +35,7 @@ module Spree 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 } + it { subject.can_manage_order_cycles?(user).should be_true } end context "as manager of an enterprise who sells 'none'" do @@ -44,6 +46,7 @@ module Spree it { subject.can_manage_products?(user).should be_false } it { subject.can_manage_enterprises?(user).should be_true } it { subject.can_manage_orders?(user).should be_false } + it { subject.can_manage_order_cycles?(user).should be_false } end context "as manager of a producer enterprise who sells 'any'" do @@ -54,6 +57,7 @@ module Spree 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 } + it { subject.can_manage_order_cycles?(user).should be_true } end context "as manager of a producer enterprise who sell 'own'" do @@ -64,6 +68,7 @@ module Spree 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 } + it { subject.can_manage_order_cycles?(user).should be_true } end context "as manager of a producer enterprise who sells 'none'" do @@ -81,6 +86,7 @@ module Spree 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 } + it { subject.can_manage_order_cycles?(user).should be_false } end context "as a profile" do @@ -93,6 +99,7 @@ module Spree it { subject.can_manage_products?(user).should be_false } it { subject.can_manage_enterprises?(user).should be_true } it { subject.can_manage_orders?(user).should be_false } + it { subject.can_manage_order_cycles?(user).should be_false } end end @@ -100,6 +107,7 @@ module Spree 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 { subject.can_manage_order_cycles?(user).should be_false } it "can create enterprises straight off the bat" do subject.is_new_user?(user).should be_true @@ -212,6 +220,40 @@ module Spree should_not have_ability([:sales_total, :group_buys, :payments, :orders_and_distributors, :users_and_enterprises], for: :report) end + describe "order_cycles abilities" do + context "where the enterprise is not in an order_cycle" do + let!(:order_cycle) { create(:simple_order_cycle) } + + it "should not be able to access read/update order_cycle actions" do + should_not have_ability([:admin, :index, :read, :edit, :update], for: order_cycle) + end + + it "should not be able to access bulk_update, clone order cycle actions" do + should_not have_ability([:bulk_update, :clone], for: order_cycle) + end + + it "cannot request permitted enterprises for an order cycle" do + should_not have_ability([:for_order_cycle], for: Enterprise) + end + end + + context "where the enterprise is in an order_cycle" do + let!(:order_cycle) { create(:simple_order_cycle) } + let!(:exchange){ create(:exchange, incoming: true, order_cycle: order_cycle, receiver: order_cycle.coordinator, sender: s1) } + + it "should be able to access read/update order cycle actions" do + should have_ability([:admin, :index, :read, :edit, :update], for: order_cycle) + end + + it "should not be able to access bulk/update, clone order cycle actions" do + should_not have_ability([:bulk_update, :clone], for: order_cycle) + end + + it "can request permitted enterprises for an order cycle" do + should have_ability([:for_order_cycle], for: Enterprise) + end + end + end end context "when is a distributor enterprise user" do @@ -357,6 +399,22 @@ module Spree should_not have_ability([:sales_total, :users_and_enterprises], for: :report) end + context "for a given order_cycle" do + let!(:order_cycle) { create(:simple_order_cycle) } + let!(:exchange){ create(:exchange, incoming: false, order_cycle: order_cycle, receiver: d1, sender: order_cycle.coordinator) } + + it "should be able to access read and update order cycle actions" do + should have_ability([:admin, :index, :read, :edit, :update], for: order_cycle) + end + + it "should not be able to access bulk_update, clone order cycle actions" do + should_not have_ability([:bulk_update, :clone], for: order_cycle) + end + end + + it "can request permitted enterprises for an order cycle" do + should have_ability([:for_order_cycle], for: Enterprise) + end end context 'Order Cycle co-ordinator, distributor enterprise manager' do @@ -371,11 +429,11 @@ module Spree let(:oc2) { create(:simple_order_cycle) } it "should be able to read/write OrderCycles they are the co-ordinator of" do - should have_ability([:admin, :index, :read, :edit, :update, :clone, :destroy], for: oc1) + should have_ability([:admin, :index, :read, :edit, :update, :bulk_update, :clone, :destroy], for: oc1) end it "should not be able to read/write OrderCycles they are not the co-ordinator of" do - should_not have_ability([:admin, :index, :read, :create, :edit, :update, :clone, :destroy], for: oc2) + should_not have_ability([:admin, :index, :read, :create, :edit, :update, :bulk_update, :clone, :destroy], for: oc2) end it "should be able to create OrderCycles" do From df4437ecfe987883e21d196cd03bd23baa40f7e7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 18 Mar 2015 16:29:22 +1100 Subject: [PATCH 249/384] Altering permissions for exchanges, so the exchange is visible to managers of at least one of the enterprises invloved --- lib/open_food_network/permissions.rb | 10 ++++++++-- spec/lib/open_food_network/permissions_spec.rb | 16 ++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index b917d1fc7d..aa47fef16a 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -55,8 +55,9 @@ module OpenFoodNetwork # Find the exchanges of an order cycle that an admin can manage def order_cycle_exchanges(order_cycle) - enterprises = managed_and_related_enterprises_with :add_to_order_cycle - order_cycle.exchanges.to_enterprises(enterprises).from_enterprises(enterprises) + ids = order_cycle_exchange_ids_involving_my_enterprises(order_cycle) + + Exchange.where(id: ids, order_cycle_id: order_cycle) end def managed_products @@ -103,5 +104,10 @@ module OpenFoodNetwork def related_enterprise_products Spree::Product.where('supplier_id IN (?)', related_enterprises_with(:manage_products)) end + + def order_cycle_exchange_ids_involving_my_enterprises(order_cycle) + # Any exchanges that my managed enterprises are involved in directly + order_cycle.exchanges.involving(managed_enterprises).pluck :id + end end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index f28956d34b..4c2c34a5f1 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -122,19 +122,19 @@ module OpenFoodNetwork permissions.order_cycle_exchanges(oc).should == [ex] end - it "returns exchanges involving enterprises with E2E permission" do + it "does not return exchanges involving enterprises with E2E permission" do permissions.stub(:related_enterprises_with) { Enterprise.where(id: [e1, e2]) } + permissions.order_cycle_exchanges(oc).should == [] + end + + it "returns exchanges involving only the sender" do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } permissions.order_cycle_exchanges(oc).should == [ex] end - it "does not return exchanges involving only the sender" do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - permissions.order_cycle_exchanges(oc).should == [] - end - - it "does not return exchanges involving only the receiver" do + it "returns exchanges involving only the receiver" do permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - permissions.order_cycle_exchanges(oc).should == [] + permissions.order_cycle_exchanges(oc).should == [ex] end end From 6c4db7fc226c3766a9b4e4aebbedd0f13fd8e225 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 18 Mar 2015 17:50:12 +1100 Subject: [PATCH 250/384] Adding permission to view outgoing exchanges to producers who have granted P-OC to the relevant outgoing hub --- lib/open_food_network/permissions.rb | 27 ++++++++++++++- .../lib/open_food_network/permissions_spec.rb | 33 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index aa47fef16a..62e202395f 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -55,7 +55,7 @@ module OpenFoodNetwork # Find the exchanges of an order cycle that an admin can manage def order_cycle_exchanges(order_cycle) - ids = order_cycle_exchange_ids_involving_my_enterprises(order_cycle) + ids = order_cycle_exchange_ids_involving_my_enterprises(order_cycle) | order_cycle_exchange_ids_distributing_my_variants(order_cycle) Exchange.where(id: ids, order_cycle_id: order_cycle) end @@ -97,6 +97,16 @@ module OpenFoodNetwork Enterprise.where('id IN (?)', parent_ids) end + # Related enterprises receiving 'permission' FROM 'enterprises' + def related_enterprises_receiving(permission, enterprises) + child_ids = EnterpriseRelationship. + permitted_by(enterprises). + with_permission(permission). + pluck(:child_id) + + Enterprise.where('id IN (?)', child_ids) + end + def managed_enterprise_products Spree::Product.managed_by(@user) end @@ -109,5 +119,20 @@ module OpenFoodNetwork # Any exchanges that my managed enterprises are involved in directly order_cycle.exchanges.involving(managed_enterprises).pluck :id end + + def order_cycle_exchange_ids_distributing_my_variants(order_cycle) + # Any outgoing exchange where the distributor has been granted P-OC by one or more of my producers + hubs = related_enterprises_receiving(:add_to_order_cycle, managed_enterprises.is_primary_producer).is_hub.pluck(:id) + permitted_exchanges = order_cycle.exchanges.outgoing.where(receiver_id: hubs) + + # TODO: remove active_exchanges when we think it is safe to do so + # active_exchanges is for backward compatability, before we restricted variants in each + # outgoing exchange to those where the producer had granted P-OC to the distributor + # For any of my managed producers, any outgoing exchanges with their variants + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants).pluck :id + + permitted_exchanges | active_exchanges + end end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 4c2c34a5f1..009c024a78 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -136,6 +136,39 @@ module OpenFoodNetwork permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } permissions.order_cycle_exchanges(oc).should == [ex] end + + describe "special permissions for managers of producers" do + let!(:producer) { create(:supplier_enterprise) } + before do + ex.incoming = false + ex.save + end + + it "returns outgoing exchanges where the hub has been granted P-OC by a supplier I manage" do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + create(:enterprise_relationship, parent: producer, child: e2, permissions_list: [:add_to_order_cycle]) + + permissions.order_cycle_exchanges(oc).should == [ex] + end + + + it "does not return outgoing exchanges where only the coordinator has been granted P-OC by a producer I manage" do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + create(:enterprise_relationship, parent: producer, child: e1, permissions_list: [:add_to_order_cycle]) + + permissions.order_cycle_exchanges(oc).should == [] + end + + # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required + it "returns outgoing exchanges which include variants produced by a producer I manage" do + product = create(:product, supplier: producer) + variant = create(:variant, product: product) + ex.variants << variant + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + + permissions.order_cycle_exchanges(oc).should == [ex] + end + end end describe "finding managed products" do From 22161bfd6ed3191b5fe78a5ba05bc42e8ef2e262 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Mar 2015 10:33:32 +1100 Subject: [PATCH 251/384] rewriting permissions query functions in permissions library, to make scoping easier --- lib/open_food_network/permissions.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 62e202395f..183826994d 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -52,7 +52,6 @@ module OpenFoodNetwork permissions end - # Find the exchanges of an order cycle that an admin can manage def order_cycle_exchanges(order_cycle) ids = order_cycle_exchange_ids_involving_my_enterprises(order_cycle) | order_cycle_exchange_ids_distributing_my_variants(order_cycle) @@ -97,14 +96,22 @@ module OpenFoodNetwork Enterprise.where('id IN (?)', parent_ids) end - # Related enterprises receiving 'permission' FROM 'enterprises' - def related_enterprises_receiving(permission, enterprises) + def granting(permission, options={}) + parent_ids = EnterpriseRelationship. + permitting(options[:to] || managed_enterprises). + with_permission(permission). + pluck(:parent_id) + + (options[:scope] || Enterprise).where('id IN (?)', parent_ids) + end + + def granted(permission, options={}) child_ids = EnterpriseRelationship. - permitted_by(enterprises). + permitted_by(options[:by] || managed_enterprises). with_permission(permission). pluck(:child_id) - Enterprise.where('id IN (?)', child_ids) + (options[:scope] || Enterprise).where('id IN (?)', child_ids) end def managed_enterprise_products @@ -122,7 +129,7 @@ module OpenFoodNetwork def order_cycle_exchange_ids_distributing_my_variants(order_cycle) # Any outgoing exchange where the distributor has been granted P-OC by one or more of my producers - hubs = related_enterprises_receiving(:add_to_order_cycle, managed_enterprises.is_primary_producer).is_hub.pluck(:id) + hubs = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer, scope: Enterprise.is_hub).pluck(:id) permitted_exchanges = order_cycle.exchanges.outgoing.where(receiver_id: hubs) # TODO: remove active_exchanges when we think it is safe to do so From 252ddf9bebc73a77954094433639512fcf693f67 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Mar 2015 11:23:44 +1100 Subject: [PATCH 252/384] Adding method to permissions library for scoping the list of visible variants in a given exchange to a particular user --- lib/open_food_network/permissions.rb | 30 +++++ .../lib/open_food_network/permissions_spec.rb | 120 ++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 183826994d..509d4dc2d4 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -59,6 +59,36 @@ module OpenFoodNetwork Exchange.where(id: ids, order_cycle_id: order_cycle) end + # Find the variants within an exchange that a user can POTENTIALLY see + # Note that this does not determine whether they actually appear in outgoing exchanges + # as this requires first that the variant is included in an incoming exchange + def visible_variants_within(exchange) + if exchange.incoming + if managed_enterprises.pluck(:id).include?(exchange.receiver_id) || managed_enterprises.pluck(:id).include?(exchange.sender_id) + # All variants belonging to the producer + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', exchange.sender_id) + else + [] # None + end + else + if managed_enterprises.pluck(:id).include?(exchange.receiver_id) || managed_enterprises.pluck(:id).include?(exchange.sender_id) + # Any variants of any producers that have granted the receiver P-OC + producers = granting(:add_to_order_cycle, to: [exchange.receiver], scope: Enterprise.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + # PLUS any variants that are already in the exchange, so things don't break + active_variants = exchange.variants + Spree::Variant.where(id: permitted_variants | active_variants) + else + # Any variants produced by MY PRODUCERS, where my producer has granted P-OC to the receiver + producers = granting(:add_to_order_cycle, to: [exchange.receiver], scope: managed_enterprises.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + # PLUS any of my producers variants that are already in the exchange, so things don't break + active_variants = exchange.variants.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + Spree::Variant.where(id: permitted_variants | active_variants) + end + end + end + def managed_products managed_enterprise_products_ids = managed_enterprise_products.pluck :id permitted_enterprise_products_ids = related_enterprise_products.pluck :id diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 009c024a78..cea24c5c52 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -171,6 +171,126 @@ module OpenFoodNetwork end end + describe "finding the variants within a given exchange which are visible to a user" do + let!(:producer1) { create(:supplier_enterprise) } + let!(:producer2) { create(:supplier_enterprise) } + let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } + let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } + let(:oc) { create(:simple_order_cycle) } + + describe "incoming exchanges" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: e1, incoming: true) } + + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.visible_variants_within(ex) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.visible_variants_within(ex) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of a hub which has been granted P-OC by the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns no variants" do + visible = permissions.visible_variants_within(ex) + expect(visible).to eq [] + end + end + end + + describe "outgoing exchanges" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.visible_variants_within(ex) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as manager of the outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.visible_variants_within(ex) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as the manager of a producer which has granted P-OC to the outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns all of my produced variants" do + visible = permissions.visible_variants_within(ex) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as the manager of a producer which has not granted P-OC to the outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns an empty array" do + expect(permissions.visible_variants_within(ex)).to eq [] + end + end + + # TODO: for backwards compatability, remove later + context "as the manager of a producer which has not granted P-OC to the outgoing hub, but which has variants already in the exchange" do + # This one won't be in the exchange, and so shouldn't be visible + let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + ex.variants << v2 + end + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_within(ex) + expect(visible).to_not include v1, v3 + expect(visible).to include v2 + end + end + end + end + describe "finding managed products" do let!(:p1) { create(:simple_product) } let!(:p2) { create(:simple_product) } From 00821a60cc1a34ef92209600ddb8b19dd46f4f3b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Mar 2015 11:34:04 +1100 Subject: [PATCH 253/384] More specific references to enterprise id in permissions querying methods --- lib/open_food_network/permissions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 509d4dc2d4..593f54d4a5 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -132,7 +132,7 @@ module OpenFoodNetwork with_permission(permission). pluck(:parent_id) - (options[:scope] || Enterprise).where('id IN (?)', parent_ids) + (options[:scope] || Enterprise).where('enterprises.id IN (?)', parent_ids) end def granted(permission, options={}) @@ -141,7 +141,7 @@ module OpenFoodNetwork with_permission(permission). pluck(:child_id) - (options[:scope] || Enterprise).where('id IN (?)', child_ids) + (options[:scope] || Enterprise).where('enterprises.id IN (?)', child_ids) end def managed_enterprise_products From 1c09b8b76f1e927addde6e23c919ffe44cb465ba Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Mar 2015 11:35:38 +1100 Subject: [PATCH 254/384] Limiting variants loaded into representative template to those a visible to the current user --- app/views/admin/order_cycles/show.rep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/order_cycles/show.rep b/app/views/admin/order_cycles/show.rep index fb71602eb6..c38bb99e85 100644 --- a/app/views/admin/order_cycles/show.rep +++ b/app/views/admin/order_cycles/show.rep @@ -15,7 +15,7 @@ r.element :order_cycle, @order_cycle do r.element :receiver_id r.element :incoming - r.element :variants, Hash[ exchange.variants.map { |v| [v.id, true] } ], {} + r.element :variants, Hash[ OpenFoodNetwork::Permissions.new(spree_current_user).visible_variants_within(exchange).map { |v| [v.id, true] } ], {} r.list_of :enterprise_fees do |fee| r.element :id From e9f2e743ce63e906c27f96b2fe57ecc22f49e3ce Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Mar 2015 12:17:09 +1100 Subject: [PATCH 255/384] Replacing old representative template for OCs with active model serializers --- .../admin/order_cycles_controller.rb | 4 ++- .../api/admin/enterprise_fee_serializer.rb | 3 ++ .../api/admin/exchange_serializer.rb | 12 ++++++++ .../api/admin/order_cycle_serializer.rb | 18 ++++++++++++ app/views/admin/order_cycles/show.rep | 28 ------------------- 5 files changed, 36 insertions(+), 29 deletions(-) create mode 100644 app/serializers/api/admin/enterprise_fee_serializer.rb create mode 100644 app/serializers/api/admin/exchange_serializer.rb create mode 100644 app/serializers/api/admin/order_cycle_serializer.rb delete mode 100644 app/views/admin/order_cycles/show.rep diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index f7c623c6eb..d42665cbd2 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -13,7 +13,9 @@ module Admin def show respond_to do |format| format.html - format.json + format.json do + render json: Api::Admin::OrderCycleSerializer.new(@order_cycle, current_user: spree_current_user).to_json + end end end diff --git a/app/serializers/api/admin/enterprise_fee_serializer.rb b/app/serializers/api/admin/enterprise_fee_serializer.rb new file mode 100644 index 0000000000..5f5755507e --- /dev/null +++ b/app/serializers/api/admin/enterprise_fee_serializer.rb @@ -0,0 +1,3 @@ +class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer + attributes :id, :enterprise_id +end diff --git a/app/serializers/api/admin/exchange_serializer.rb b/app/serializers/api/admin/exchange_serializer.rb new file mode 100644 index 0000000000..34d5c3cf9b --- /dev/null +++ b/app/serializers/api/admin/exchange_serializer.rb @@ -0,0 +1,12 @@ +class Api::Admin::ExchangeSerializer < ActiveModel::Serializer + attributes :id, :sender_id, :receiver_id, :incoming, :variants, :pickup_time, :pickup_instructions + + has_many :enterprise_fees, serializer: Api::Admin::EnterpriseFeeSerializer + + def variants + Hash[ + OpenFoodNetwork::Permissions.new(options[:current_user]). + visible_variants_within(object).map { |v| [v.id, true] } + ] + end +end diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb new file mode 100644 index 0000000000..a4c129774e --- /dev/null +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -0,0 +1,18 @@ +class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer + attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges + + has_many :coordinator_fees, serializer: Api::IdSerializer + + def orders_open_at + object.orders_open_at.to_s + end + + def orders_close_at + object.orders_close_at.to_s + end + + def exchanges + scoped_exchanges = OpenFoodNetwork::Permissions.new(options[:current_user]).order_cycle_exchanges(object).order('id ASC') + ActiveModel::ArraySerializer.new(scoped_exchanges, {each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user] }) + end +end diff --git a/app/views/admin/order_cycles/show.rep b/app/views/admin/order_cycles/show.rep deleted file mode 100644 index c38bb99e85..0000000000 --- a/app/views/admin/order_cycles/show.rep +++ /dev/null @@ -1,28 +0,0 @@ -r.element :order_cycle, @order_cycle do - r.element :id - r.element :name - r.element :orders_open_at, @order_cycle.orders_open_at.to_s - r.element :orders_close_at, @order_cycle.orders_close_at.to_s - - r.element :coordinator_id - r.list_of :coordinator_fees do |fee| - r.element :id - end - - r.list_of :exchanges, OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_exchanges(@order_cycle).order('id ASC') do |exchange| - r.element :id - r.element :sender_id - r.element :receiver_id - r.element :incoming - - r.element :variants, Hash[ OpenFoodNetwork::Permissions.new(spree_current_user).visible_variants_within(exchange).map { |v| [v.id, true] } ], {} - - r.list_of :enterprise_fees do |fee| - r.element :id - r.element :enterprise_id - end - - r.element :pickup_time - r.element :pickup_instructions - end -end From 1927bc55d0f45929e638c79cd0c570562779dace Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Mar 2015 17:04:43 +1100 Subject: [PATCH 256/384] Load actual variants in the exchange, wrong behaviour was implemented in previous two commits --- app/serializers/api/admin/exchange_serializer.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/serializers/api/admin/exchange_serializer.rb b/app/serializers/api/admin/exchange_serializer.rb index 34d5c3cf9b..c9fcad75db 100644 --- a/app/serializers/api/admin/exchange_serializer.rb +++ b/app/serializers/api/admin/exchange_serializer.rb @@ -4,9 +4,6 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer has_many :enterprise_fees, serializer: Api::Admin::EnterpriseFeeSerializer def variants - Hash[ - OpenFoodNetwork::Permissions.new(options[:current_user]). - visible_variants_within(object).map { |v| [v.id, true] } - ] + Hash[ object.variants.map { |v| [v.id, true] } ] end end From 4dff3fe79cabd00ef8cffb2e4b90f1ef77595876 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Mar 2015 17:05:47 +1100 Subject: [PATCH 257/384] Removing unused distributable action from products api --- .../spree/api/products_controller_decorator.rb | 9 --------- config/routes.rb | 1 - 2 files changed, 10 deletions(-) diff --git a/app/controllers/spree/api/products_controller_decorator.rb b/app/controllers/spree/api/products_controller_decorator.rb index af7834bb04..0186c6e8df 100644 --- a/app/controllers/spree/api/products_controller_decorator.rb +++ b/app/controllers/spree/api/products_controller_decorator.rb @@ -20,15 +20,6 @@ Spree::Api::ProductsController.class_eval do render_paged_products @products end - def distributable - producers = OpenFoodNetwork::Permissions.new(current_api_user). - order_cycle_enterprises.is_primary_producer.by_name - - @products = paged_products_for_producers producers - - render_paged_products @products - end - def overridable producers = OpenFoodNetwork::Permissions.new(current_api_user). variant_override_producers.by_name diff --git a/config/routes.rb b/config/routes.rb index 517d78e602..a7fe216d64 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -144,7 +144,6 @@ Spree::Core::Engine.routes.prepend do collection do get :managed get :bulk_products - get :distributable get :overridable end delete :soft_delete From 81765de7a774feab00597f9cf8b5445806ead562 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Mar 2015 23:46:38 +1100 Subject: [PATCH 258/384] Building a permission method to determine which enterprises are visible to the user for a given OC --- lib/open_food_network/permissions.rb | 29 ++++ .../lib/open_food_network/permissions_spec.rb | 126 ++++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 593f54d4a5..9e8cdcafa1 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -15,6 +15,35 @@ module OpenFoodNetwork managed_and_related_enterprises_with :add_to_order_cycle end + # List of any enterprises whose exchanges I should be able to see in order_cycles + def enterprises_for(order_cycle) + # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) + # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) + coordinator_permitted = [] + if managed_enterprises.include? order_cycle.coordinator + coordinator_permitted = granting(:add_to_order_cycle, to: [order_cycle.coordinator]).pluck(:id) + coordinator_permitted << order_cycle.coordinator + end + + # Any enterprises that I manage directly, which have granted P-OC to the coordinator + managed_permitted = granting(:add_to_order_cycle, to: [order_cycle.coordinator], scope: managed_enterprises).pluck(:id) + + # TODO: remove this when permissions are all sorted out + # Any enterprises that I manage that are already in the order_cycle + managed_active = managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors).pluck(:id) + + # Any hubs that have been granted P-OC by producers I manage + hubs_permitted = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer, scope: Enterprise.is_hub).pluck(:id) + + # TODO: Remove this when all P-OC are sorted out + # Any hubs that currently have outgoing exchanges distributing variants of producers I manage + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants) + hubs_active = active_exchanges.map(&:receiver_id) + + Enterprise.where(id: coordinator_permitted | managed_permitted | managed_active | hubs_permitted | hubs_active) + end + # Find enterprises for which an admin is allowed to edit their profile def editable_enterprises managed_and_related_enterprises_with :edit_profile diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index cea24c5c52..0a66815e75 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -21,6 +21,132 @@ module OpenFoodNetwork end end + describe "finding enterprises that can be viewed in the order cycle interface" do + let(:coordinator) { create(:distributor_enterprise) } + let(:hub) { create(:distributor_enterprise) } + let(:producer) { create(:supplier_enterprise) } + let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } + + context "as a manager of the coordinator" do + it "returns enterprises which have granted P-OC to the coordinator" do + create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to include hub + expect(enterprises).to_not include producer + end + + it "returns the coordinator itself" do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + expect(permissions.enterprises_for(oc)).to include coordinator + end + end + + context "as a manager of a hub that has granted P-OC to the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + end + + context "that has granted P-OC to the coordinator" do + before do + create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) + end + + it "returns my hub" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to include hub + expect(enterprises).to_not include producer, coordinator + end + + context "and has also granted P-OC to a producer" do + before do + create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) + end + it "does not return that producer" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to_not include producer + end + end + end + + context "that has not granted P-OC to the coordinator" do + it "does not return my hub" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to_not include hub, producer, coordinator + end + + context "but is already in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns my hub" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to include hub + expect(enterprises).to_not include producer, coordinator + end + end + end + end + + context "as a manager of a producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + end + + context "which has granted P-OC to the coordinator" do + before do + create(:enterprise_relationship, parent: producer, child: coordinator, permissions_list: [:add_to_order_cycle]) + end + + it "returns my producer, and the coordindator itself" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to include producer, coordinator + expect(enterprises).to_not include hub + end + + context "and has also granted P-OC to a hub" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns that hub as well" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to include producer, coordinator, hub + end + end + end + + context "which has not granted P-OC to the coordinator" do + it "does not return my producer" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to_not include producer + end + + context "but is already in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + # TODO: update this when we are confident about P-OCs + it "returns my producer" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to include producer + expect(enterprises).to_not include hub, coordinator + end + + context "and has variants distributed by an outgoing hub" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + before { ex_outgoing.variants << create(:variant, product: create(:product, supplier: producer)) } + + # TODO: update this when we are confident about P-OCs + it "returns that hub as well" do + enterprises = permissions.enterprises_for(oc) + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + end + end + end + end + describe "finding enterprises whose profiles can be edited" do let(:e) { double(:enterprise) } From d8c4e292c8bce01543a936b9bc2c9d9ca3fc7249 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Mar 2015 00:32:05 +1100 Subject: [PATCH 259/384] Adding method for determining variant override hubs --- app/controllers/admin/variant_overrides_controller.rb | 4 ++-- app/models/spree/ability_decorator.rb | 2 +- lib/open_food_network/permissions.rb | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index c89a8e8dd2..9425565f6e 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -2,7 +2,6 @@ require 'open_food_network/spree_api_key_loader' module Admin class VariantOverridesController < ResourceController - include OrderCyclesHelper include OpenFoodNetwork::SpreeApiKeyLoader before_filter :load_spree_api_key, only: :index @@ -35,7 +34,8 @@ module Admin private def load_data - @hubs = order_cycle_hub_enterprises + @hubs = OpenFoodNetwork::Permissions.new(spree_current_user). + variant_override_hubs.by_name # Used in JS to look up the name of the producer of each product @producers = OpenFoodNetwork::Permissions.new(spree_current_user). diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 5953d56ffd..09d46c523e 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -103,7 +103,7 @@ class AbilityDecorator can [:admin, :index, :read, :update, :bulk_update], VariantOverride do |vo| hub_auth = OpenFoodNetwork::Permissions.new(user). - order_cycle_enterprises.is_distributor. + variant_override_hubs. include? vo.hub producer_auth = OpenFoodNetwork::Permissions.new(user). diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 9e8cdcafa1..3fa25884e0 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -49,6 +49,10 @@ module OpenFoodNetwork managed_and_related_enterprises_with :edit_profile end + def variant_override_hubs + managed_and_related_enterprises_with(:add_to_order_cycle).is_hub + end + def variant_override_producers producer_ids = variant_override_enterprises_per_hub.values.flatten.uniq Enterprise.where(id: producer_ids) From c9f343f6802227a77b97c951f995f123939bb350 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Mar 2015 00:33:39 +1100 Subject: [PATCH 260/384] Swapping over scoping method for displayed enterprises in OC index page from order_cycle_enterprises to enterprises_for(order_cycle) --- app/controllers/admin/order_cycles_controller.rb | 1 - app/views/admin/order_cycles/_row.html.haml | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index d42665cbd2..20c844fe96 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -89,7 +89,6 @@ module Admin private def load_data_for_index @show_more = !!params[:show_more] - @order_cycle_enterprises = OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises @order_cycle_set = OrderCycleSet.new :collection => collection(@show_more) end diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 588ebdd745..e241cf8dbb 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -8,7 +8,7 @@ - unless order_cycles_simple_index %td.suppliers - - suppliers = order_cycle.suppliers.merge(@order_cycle_enterprises) + - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).enterprises_for(order_cycle)) - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 %span.with-tip{'data-powertip' => supplier_list} @@ -18,7 +18,7 @@ = supplier_list %td= order_cycle.coordinator.name %td.distributors - - distributors = order_cycle.distributors.merge(@order_cycle_enterprises) + - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).enterprises_for(order_cycle)) - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 %span.with-tip{'data-powertip' => distributor_list} @@ -33,7 +33,7 @@ %td.actions = link_to '', main_app.edit_admin_order_cycle_path(order_cycle), class: 'edit-order-cycle icon-edit no-text' %td.actions - = link_to '', main_app.clone_admin_order_cycle_path(order_cycle), class: 'clone-order-cycle icon-copy no-text' + = link_to '', main_app.clone_admin_order_cycle_path(order_cycle), class: 'clone-order-cycle icon-copy no-text' - if can_delete?(order_cycle) %td.actions = link_to '', main_app.admin_order_cycle_path(order_cycle), class: 'delete-order-cycle icon-trash no-text', :method => :delete, data: { confirm: "Are you sure?" } From 7e24e6743ecf02ad422e0c5bb371a98394fa1adc Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Mar 2015 12:22:46 +1100 Subject: [PATCH 261/384] enterprises_for is now order_cycle_enterprises_for, and can be passed an order_cycle or a coordinator (for new order_cycles) --- app/views/admin/order_cycles/_row.html.haml | 4 +- lib/open_food_network/permissions.rb | 47 +++++++++++++------ .../lib/open_food_network/permissions_spec.rb | 22 ++++----- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index e241cf8dbb..58986b280e 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -8,7 +8,7 @@ - unless order_cycles_simple_index %td.suppliers - - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).enterprises_for(order_cycle)) + - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).enterprises_for(order_cycle: order_cycle))) - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 %span.with-tip{'data-powertip' => supplier_list} @@ -18,7 +18,7 @@ = supplier_list %td= order_cycle.coordinator.name %td.distributors - - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).enterprises_for(order_cycle)) + - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).enterprises_for(order_cycle: order_cycle))) - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 %span.with-tip{'data-powertip' => distributor_list} diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 3fa25884e0..3cc0f5b704 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -15,31 +15,48 @@ module OpenFoodNetwork managed_and_related_enterprises_with :add_to_order_cycle end - # List of any enterprises whose exchanges I should be able to see in order_cycles - def enterprises_for(order_cycle) + # List of any enterprises whose exchanges I should be able to see in order_cycle + # NOTE: the enterprises a given user can see actually in the OC interface depend on the relationships + # of their enterprises to the coordinator of the order cycle, rather than on the order cycle itself + # (until such time as we implement friends of friends) + def order_cycle_enterprises_for(options={}) + # Can provide a coordinator OR an order cycle. Use just coordinator for new order cycles + # if both are provided, coordinator will be ignored, and the coordinator of the OC will be used + return Enterprise.where("1=0") unless options[:coordinator] || options[:order_cycle] + coordinator = options[:coordinator] + order_cycle = nil + if options[:order_cycle] + order_cycle = options[:order_cycle] + coordinator = order_cycle.coordinator + end + # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) coordinator_permitted = [] - if managed_enterprises.include? order_cycle.coordinator - coordinator_permitted = granting(:add_to_order_cycle, to: [order_cycle.coordinator]).pluck(:id) - coordinator_permitted << order_cycle.coordinator + if managed_enterprises.include? coordinator + coordinator_permitted = granting(:add_to_order_cycle, to: [coordinator]).pluck(:id) + coordinator_permitted << coordinator end # Any enterprises that I manage directly, which have granted P-OC to the coordinator - managed_permitted = granting(:add_to_order_cycle, to: [order_cycle.coordinator], scope: managed_enterprises).pluck(:id) - - # TODO: remove this when permissions are all sorted out - # Any enterprises that I manage that are already in the order_cycle - managed_active = managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors).pluck(:id) + managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises).pluck(:id) # Any hubs that have been granted P-OC by producers I manage hubs_permitted = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer, scope: Enterprise.is_hub).pluck(:id) - # TODO: Remove this when all P-OC are sorted out - # Any hubs that currently have outgoing exchanges distributing variants of producers I manage - variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) - active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants) - hubs_active = active_exchanges.map(&:receiver_id) + managed_active = [] + hubs_active = [] + if order_cycle + # TODO: remove this when permissions are all sorted out + # Any enterprises that I manage that are already in the order_cycle + managed_active = managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors).pluck(:id) + + # TODO: Remove this when all P-OC are sorted out + # Any hubs that currently have outgoing exchanges distributing variants of producers I manage + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants) + hubs_active = active_exchanges.map(&:receiver_id) + end Enterprise.where(id: coordinator_permitted | managed_permitted | managed_active | hubs_permitted | hubs_active) end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 0a66815e75..996380bc69 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -31,14 +31,14 @@ module OpenFoodNetwork it "returns enterprises which have granted P-OC to the coordinator" do create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to include hub expect(enterprises).to_not include producer end it "returns the coordinator itself" do permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } - expect(permissions.enterprises_for(oc)).to include coordinator + expect(permissions.order_cycle_enterprises_for(order_cycle: oc)).to include coordinator end end @@ -53,7 +53,7 @@ module OpenFoodNetwork end it "returns my hub" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to include hub expect(enterprises).to_not include producer, coordinator end @@ -63,7 +63,7 @@ module OpenFoodNetwork create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) end it "does not return that producer" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to_not include producer end end @@ -71,7 +71,7 @@ module OpenFoodNetwork context "that has not granted P-OC to the coordinator" do it "does not return my hub" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to_not include hub, producer, coordinator end @@ -79,7 +79,7 @@ module OpenFoodNetwork let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns my hub" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to include hub expect(enterprises).to_not include producer, coordinator end @@ -98,7 +98,7 @@ module OpenFoodNetwork end it "returns my producer, and the coordindator itself" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to include producer, coordinator expect(enterprises).to_not include hub end @@ -109,7 +109,7 @@ module OpenFoodNetwork end it "returns that hub as well" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to include producer, coordinator, hub end end @@ -117,7 +117,7 @@ module OpenFoodNetwork context "which has not granted P-OC to the coordinator" do it "does not return my producer" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to_not include producer end @@ -126,7 +126,7 @@ module OpenFoodNetwork # TODO: update this when we are confident about P-OCs it "returns my producer" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to include producer expect(enterprises).to_not include hub, coordinator end @@ -137,7 +137,7 @@ module OpenFoodNetwork # TODO: update this when we are confident about P-OCs it "returns that hub as well" do - enterprises = permissions.enterprises_for(oc) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end From a1aa9512a83626cfe592118667ecebba9eb8771e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Mar 2015 15:26:19 +1100 Subject: [PATCH 262/384] Fixing angular order_cycle controller spec --- spec/javascripts/unit/order_cycle_spec.js.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 6a1e1be70f..dc445010ca 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -37,10 +37,11 @@ describe 'OrderCycle controllers', -> EnterpriseFee = index: jasmine.createSpy('index').andReturn('enterprise fees list') forEnterprise: jasmine.createSpy('forEnterprise').andReturn('enterprise fees for enterprise') + ocInstance = {} module('admin.order_cycles') inject ($controller) -> - ctrl = $controller 'AdminCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee} + ctrl = $controller 'AdminCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee, ocInstance: ocInstance} it 'Loads enterprises and supplied products', -> From 45f6042d3de820cbe719a4dc48ddd1c07b7e7f38 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Mar 2015 16:33:08 +1100 Subject: [PATCH 263/384] Enterprises etched by for_order_cycle action now use permissions scoped using an order cycle or coordinator --- .../admin/order_cycle.js.erb.coffee | 6 +-- .../controllers/simple_create.js.coffee | 2 +- .../services/enterprise.js.coffee | 26 ++++++----- .../admin/enterprises_controller.rb | 20 +++++---- .../admin/enterprises_controller_spec.rb | 45 +++++++++++++++++++ .../unit/order_cycle_spec.js.coffee | 2 +- 6 files changed, 77 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 83f6e4b080..78db9232dc 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -1,6 +1,6 @@ angular.module('admin.order_cycles', ['ngResource']) .controller('AdminCreateOrderCycleCtrl', ['$scope', 'OrderCycle', 'Enterprise', 'EnterpriseFee', 'ocInstance', ($scope, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> - $scope.enterprises = Enterprise.index() + $scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id) $scope.supplied_products = Enterprise.supplied_products $scope.enterprise_fees = EnterpriseFee.index() @@ -81,11 +81,11 @@ angular.module('admin.order_cycles', ['ngResource']) ]) .controller('AdminEditOrderCycleCtrl', ['$scope', '$location', 'OrderCycle', 'Enterprise', 'EnterpriseFee', ($scope, $location, OrderCycle, Enterprise, EnterpriseFee) -> - $scope.enterprises = Enterprise.index() + order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] + $scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id) $scope.supplied_products = Enterprise.supplied_products $scope.enterprise_fees = EnterpriseFee.index() - order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] $scope.order_cycle = OrderCycle.load(order_cycle_id) $scope.loaded = -> diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee index b93d464d74..d8281ed1d4 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee @@ -1,5 +1,5 @@ angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, OrderCycle, Enterprise, EnterpriseFee) -> - $scope.enterprises = Enterprise.index (enterprises) => + $scope.enterprises = Enterprise.index {}, (enterprises) => $scope.init(enterprises) $scope.enterprise_fees = EnterpriseFee.index() $scope.order_cycle = OrderCycle.order_cycle diff --git a/app/assets/javascripts/admin/order_cycles/services/enterprise.js.coffee b/app/assets/javascripts/admin/order_cycles/services/enterprise.js.coffee index 244d050aba..5e40f621dc 100644 --- a/app/assets/javascripts/admin/order_cycles/services/enterprise.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/enterprise.js.coffee @@ -1,24 +1,28 @@ angular.module('admin.order_cycles').factory('Enterprise', ($resource) -> - Enterprise = $resource('/admin/enterprises/for_order_cycle/:enterprise_id.json', {}, {'index': {method: 'GET', isArray: true}}) - + Enterprise = $resource('/admin/enterprises/for_order_cycle/:enterprise_id.json', {}, { + 'index': + method: 'GET' + isArray: true + params: + order_cycle_id: '@order_cycle_id' + coordinator_id: '@coordinator_id' + }) { Enterprise: Enterprise enterprises: {} supplied_products: [] loaded: false - index: (callback=null) -> - service = this - - Enterprise.index (data) -> + index: (params={}, callback=null) -> + Enterprise.index params, (data) => for enterprise in data - service.enterprises[enterprise.id] = enterprise + @enterprises[enterprise.id] = enterprise for product in enterprise.supplied_products - service.supplied_products.push(product) + @supplied_products.push(product) - service.loaded = true - (callback || angular.noop)(service.enterprises) + @loaded = true + (callback || angular.noop)(@enterprises) this.enterprises @@ -40,4 +44,4 @@ angular.module('admin.order_cycles').factory('Enterprise', ($resource) -> numVariants += if product.variants.length == 0 then 1 else product.variants.length numVariants - }) \ No newline at end of file + }) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index e48ef339b3..b4f0c70e98 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -17,10 +17,6 @@ module Admin include ActionView::Helpers::TextHelper include OrderCyclesHelper - def for_order_cycle - @collection = order_cycle_permitted_enterprises - end - def set_sells enterprise = Enterprise.find_by_permalink(params[:id]) || Enterprise.find(params[:id]) attributes = { sells: params[:sells] } @@ -94,10 +90,18 @@ module Admin end def collection - # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now - OpenFoodNetwork::Permissions.new(spree_current_user). - editable_enterprises. - order('is_primary_producer ASC, name') + case action + when :for_order_cycle + return Enterprise.where("1=0") unless params[:coordinator_id] || params[:order_cycle_id] + options = { coordinator: Enterprise.find(params[:coordinator_id]) } + options = { order_cycle: OrderCycle.find(params[:order_cycle_id]) } if params[:order_cycle_id] + return OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(options) + else + # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now + OpenFoodNetwork::Permissions.new(spree_current_user). + editable_enterprises. + order('is_primary_producer ASC, name') + end end def collection_actions diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 2262d508ef..2446768174 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -360,5 +360,50 @@ module Admin end end end + + describe "for_order_cycle" do + let!(:user) { create_enterprise_user } + let!(:enterprise) { create(:enterprise, sells: 'any', owner: user) } + let(:permission_mock) { double(:permission) } + + before do + # As a user with permission + controller.stub spree_current_user: user + Enterprise.stub find: "instance of Enterprise" + OrderCycle.stub find: "instance of OrderCycle" + + OpenFoodNetwork::Permissions.stub(:new) { permission_mock } + allow(permission_mock).to receive :order_cycle_enterprises_for + end + + context "when no order_cycle or coordinator is provided in params" do + before { spree_get :for_order_cycle } + it "returns an empty scope" do + expect(assigns(:collection)).to be_empty + expect(permission_mock).to_not have_received :order_cycle_enterprises_for + end + end + + context "when an order_cycle_id is provided in params" do + before { spree_get :for_order_cycle, order_cycle_id: 1 } + it "calls order_cycle_enterprises_for() with an :order_cycle option" do + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(order_cycle: "instance of OrderCycle") + end + end + + context "when a coordinator is provided in params" do + before { spree_get :for_order_cycle, coordinator_id: 1 } + it "calls order_cycle_enterprises_for() with a :coordinator option" do + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(coordinator: "instance of Enterprise") + end + end + + context "when both an order cycle and a coordinator are provided in params" do + before { spree_get :for_order_cycle, order_cycle_id: 1, coordinator_id: 1 } + it "calls order_cycle_enterprises_for() with an :order_cycle option" do + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(order_cycle: "instance of OrderCycle") + end + end + end end end diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index dc445010ca..43e474a49d 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -330,7 +330,7 @@ describe 'OrderCycle services', -> inject ($injector, _$httpBackend_)-> Enterprise = $injector.get('Enterprise') $httpBackend = _$httpBackend_ - $httpBackend.whenGET('/admin/enterprises/for_order_cycle.json').respond [ + $httpBackend.whenGET('/admin/enterprises/for_order_cycle.json?').respond [ {id: 1, name: 'One', supplied_products: [1, 2]} {id: 2, name: 'Two', supplied_products: [3, 4]} {id: 3, name: 'Three', supplied_products: [5, 6]} From b747f61eb153dc1cbba9adc1105397f4ff27520b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Mar 2015 17:09:26 +1100 Subject: [PATCH 264/384] P-OC permissions don't apply when determining which enterprises are loaded for order cycle interface --- lib/open_food_network/permissions.rb | 70 +++++++++++-------- .../lib/open_food_network/permissions_spec.rb | 31 +++++--- 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 3cc0f5b704..6d0a802c30 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -30,35 +30,49 @@ module OpenFoodNetwork coordinator = order_cycle.coordinator end - # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) - # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) - coordinator_permitted = [] - if managed_enterprises.include? coordinator - coordinator_permitted = granting(:add_to_order_cycle, to: [coordinator]).pluck(:id) - coordinator_permitted << coordinator + + if coordinator.sells == "own" + + # Coordinators that sell own can only see themselves in the OC interface + coordinator_permitted = [] + if managed_enterprises.include? coordinator + coordinator_permitted << coordinator + end + Enterprise.where(id: coordinator_permitted) + + else + # If the coordinator sells any, relationships come into play + + # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) + # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) + coordinator_permitted = [] + if managed_enterprises.include? coordinator + coordinator_permitted = granting(:add_to_order_cycle, to: [coordinator]).pluck(:id) + coordinator_permitted << coordinator + end + + # Any enterprises that I manage directly, which have granted P-OC to the coordinator + managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises).pluck(:id) + + # Any hubs that have been granted P-OC by producers I manage + hubs_permitted = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer, scope: Enterprise.is_hub).pluck(:id) + + managed_active = [] + hubs_active = [] + if order_cycle + # TODO: remove this when permissions are all sorted out + # Any enterprises that I manage that are already in the order_cycle + managed_active = managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors).pluck(:id) + + # TODO: Remove this when all P-OC are sorted out + # Any hubs that currently have outgoing exchanges distributing variants of producers I manage + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants) + hubs_active = active_exchanges.map(&:receiver_id) + end + + Enterprise.where(id: coordinator_permitted | managed_permitted | managed_active | hubs_permitted | hubs_active) end - - # Any enterprises that I manage directly, which have granted P-OC to the coordinator - managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises).pluck(:id) - - # Any hubs that have been granted P-OC by producers I manage - hubs_permitted = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer, scope: Enterprise.is_hub).pluck(:id) - - managed_active = [] - hubs_active = [] - if order_cycle - # TODO: remove this when permissions are all sorted out - # Any enterprises that I manage that are already in the order_cycle - managed_active = managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors).pluck(:id) - - # TODO: Remove this when all P-OC are sorted out - # Any hubs that currently have outgoing exchanges distributing variants of producers I manage - variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) - active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants) - hubs_active = active_exchanges.map(&:receiver_id) - end - - Enterprise.where(id: coordinator_permitted | managed_permitted | managed_active | hubs_permitted | hubs_active) end # Find enterprises for which an admin is allowed to edit their profile diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 996380bc69..542ca1674f 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -28,18 +28,33 @@ module OpenFoodNetwork let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } context "as a manager of the coordinator" do - it "returns enterprises which have granted P-OC to the coordinator" do - create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) - permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) - expect(enterprises).to include hub - expect(enterprises).to_not include producer - end - it "returns the coordinator itself" do permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } expect(permissions.order_cycle_enterprises_for(order_cycle: oc)).to include coordinator end + + context "where P-OC has been granted to the coordinator by other enterprises" do + before do + create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + context "where the coordinator sells own" do + it "returns enterprises which have granted P-OC to the coordinator" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include hub + expect(enterprises).to_not include producer + end + end + + context "where the coordinator sells 'own'" do + before { coordinator.stub(:sells) { 'own' } } + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to_not include hub, producer + end + end + end end context "as a manager of a hub that has granted P-OC to the coordinator" do From 35b27fcfd283bfb472077bcb11596bf805d41ae9 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Mar 2015 17:44:59 +1100 Subject: [PATCH 265/384] Delegating responsibility for returning an empty scope when no options are passed to order_cycle_enterprises_for --- app/controllers/admin/enterprises_controller.rb | 6 +++--- spec/controllers/admin/enterprises_controller_spec.rb | 7 +++---- spec/lib/open_food_network/permissions_spec.rb | 6 ++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index b4f0c70e98..7e3ab9d17a 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -92,9 +92,9 @@ module Admin def collection case action when :for_order_cycle - return Enterprise.where("1=0") unless params[:coordinator_id] || params[:order_cycle_id] - options = { coordinator: Enterprise.find(params[:coordinator_id]) } - options = { order_cycle: OrderCycle.find(params[:order_cycle_id]) } if params[:order_cycle_id] + options = {} + options[:coordinator] = Enterprise.find(params[:coordinator_id]) if params[:coordinator_id] + options[:order_cycle] = OrderCycle.find(params[:order_cycle_id]) if params[:order_cycle_id] return OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(options) else # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 2446768174..c43154f3fd 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -379,8 +379,7 @@ module Admin context "when no order_cycle or coordinator is provided in params" do before { spree_get :for_order_cycle } it "returns an empty scope" do - expect(assigns(:collection)).to be_empty - expect(permission_mock).to_not have_received :order_cycle_enterprises_for + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with({}) end end @@ -400,8 +399,8 @@ module Admin context "when both an order cycle and a coordinator are provided in params" do before { spree_get :for_order_cycle, order_cycle_id: 1, coordinator_id: 1 } - it "calls order_cycle_enterprises_for() with an :order_cycle option" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(order_cycle: "instance of OrderCycle") + it "calls order_cycle_enterprises_for() with both options" do + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(coordinator: "instance of Enterprise", order_cycle: "instance of OrderCycle") end end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 542ca1674f..185ec26d97 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -27,6 +27,12 @@ module OpenFoodNetwork let(:producer) { create(:supplier_enterprise) } let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } + context "when no order_cycle or coordinator are provided for reference" do + it "returns an empty scope" do + expect(permissions.order_cycle_enterprises_for()).to be_empty + end + end + context "as a manager of the coordinator" do it "returns the coordinator itself" do permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } From d115ef5f7ea13ad9802862eb965426d27e6435a4 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 25 Mar 2015 10:46:32 +1100 Subject: [PATCH 266/384] Scoping old generalised order cycle helper methods to a specific order cycle --- .../admin/order_cycles_controller.rb | 8 +++--- app/helpers/order_cycles_helper.rb | 26 +++++++++---------- app/views/admin/order_cycles/_form.html.haml | 6 ++--- .../order_cycles/set_coordinator.html.haml | 2 +- spec/helpers/order_cycles_helper_spec.rb | 18 ++++++------- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 20c844fe96..efb32f2483 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -31,7 +31,7 @@ module Admin respond_to do |format| if @order_cycle.save - OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, order_cycle_permitted_enterprises).go! + OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, permitted_enterprises_for(@order_cycle)).go! flash[:notice] = 'Your order cycle has been created.' format.html { redirect_to admin_order_cycles_path } @@ -48,7 +48,7 @@ module Admin respond_to do |format| if @order_cycle.update_attributes(params[:order_cycle]) - OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, order_cycle_permitted_enterprises).go! + OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, permitted_enterprises_for(@order_cycle)).go! flash[:notice] = 'Your order cycle has been updated.' format.html { redirect_to admin_order_cycles_path } @@ -93,11 +93,11 @@ module Admin end def require_coordinator - if params[:coordinator_id] && @order_cycle.coordinator = order_cycle_coordinating_enterprises.find_by_id(params[:coordinator_id]) + if params[:coordinator_id] && @order_cycle.coordinator = permitted_coordinating_enterprises_for(@order_cycle).find_by_id(params[:coordinator_id]) return end - available_coordinators = order_cycle_coordinating_enterprises.select(&:confirmed?) + available_coordinators = permitted_coordinating_enterprises_for(@order_cycle).select(&:confirmed?) case available_coordinators.count when 0 flash[:error] = "None of your enterprises have permission to coordinate an order cycle" diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 8dc887e99e..39727bbddb 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -3,32 +3,32 @@ module OrderCyclesHelper @current_order_cycle ||= current_order(false).andand.order_cycle end - def order_cycle_permitted_enterprises - OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises + def permitted_enterprises_for(order_cycle) + OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle: order_cycle) end - def order_cycle_producer_enterprises - order_cycle_permitted_enterprises.is_primary_producer.by_name + def permitted_producer_enterprises_for(order_cycle) + permitted_enterprises_for(order_cycle).is_primary_producer.by_name end - def order_cycle_producer_enterprise_options - validated_enterprise_options order_cycle_producer_enterprises, confirmed: true + def permitted_producer_enterprise_options_for(order_cycle) + validated_enterprise_options permitted_producer_enterprises_for(order_cycle), confirmed: true end - def order_cycle_coordinating_enterprises + def permitted_coordinating_enterprises_for(order_cycle) Enterprise.managed_by(spree_current_user).is_distributor.by_name end - def order_cycle_coordinating_enterprise_options - validated_enterprise_options order_cycle_coordinating_enterprises, confirmed: true + def permitted_coordinating_enterprise_options_for(order_cycle) + validated_enterprise_options permitted_coordinating_enterprises_for(order_cycle), confirmed: true end - def order_cycle_hub_enterprises - order_cycle_permitted_enterprises.is_hub.by_name + def permitted_hub_enterprises_for(order_cycle) + permitted_enterprises_for(order_cycle).is_hub.by_name end - def order_cycle_hub_enterprise_options - validated_enterprise_options order_cycle_hub_enterprises, confirmed: true, shipping_and_payment_methods: true + def permitted_hub_enterprise_options_for(order_cycle) + validated_enterprise_options permitted_hub_enterprises_for(order_cycle), confirmed: true, shipping_and_payment_methods: true end def order_cycle_status_class(order_cycle) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 387b2627ec..489055459b 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -16,7 +16,7 @@ = render 'exchange_supplied_products_form' - if spree_current_user.enterprises.include? @order_cycle.coordinator - = select_tag :new_supplier_id, options_for_select(order_cycle_producer_enterprise_options), {'ng-model' => 'new_supplier_id'} + = select_tag :new_supplier_id, options_for_select(permitted_producer_enterprise_options_for(@order_cycle)), {'ng-model' => 'new_supplier_id'} = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' @@ -24,7 +24,7 @@ %h2 Coordinator -if spree_current_user.enterprises.include? @order_cycle.coordinator = f.label :coordinator_id, 'Coordinator' - = f.select :coordinator_id, order_cycle_coordinating_enterprise_options, { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} + = f.select :coordinator_id, permitted_coordinating_enterprise_options_for(@order_cycle), { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} = render 'coordinator_fees', f: f - else = @order_cycle.coordinator.name @@ -45,7 +45,7 @@ = render 'exchange_distributed_products_form' - if spree_current_user.enterprises.include? @order_cycle.coordinator - = select_tag :new_distributor_id, options_for_select(order_cycle_hub_enterprise_options), {'ng-model' => 'new_distributor_id'} + = select_tag :new_distributor_id, options_for_select(permitted_hub_enterprise_options_for(@order_cycle)), {'ng-model' => 'new_distributor_id'} = f.submit 'Add distributor', 'ng-click' => 'addDistributor($event)' .actions diff --git a/app/views/admin/order_cycles/set_coordinator.html.haml b/app/views/admin/order_cycles/set_coordinator.html.haml index ebf0cffbb8..f2663cbfd2 100644 --- a/app/views/admin/order_cycles/set_coordinator.html.haml +++ b/app/views/admin/order_cycles/set_coordinator.html.haml @@ -7,7 +7,7 @@ .two.columns.alpha   .ten.columns - = select_tag :coordinator_id, options_for_select(order_cycle_coordinating_enterprise_options), { 'required' => true, class: 'select2 fullwidth'} + = select_tag :coordinator_id, options_for_select(permitted_coordinating_enterprise_options_for(@order_cycle)), { 'required' => true, class: 'select2 fullwidth'} .two.columns.alpha = f.submit "Continue >" diff --git a/spec/helpers/order_cycles_helper_spec.rb b/spec/helpers/order_cycles_helper_spec.rb index 0e1ea42121..6841889313 100644 --- a/spec/helpers/order_cycles_helper_spec.rb +++ b/spec/helpers/order_cycles_helper_spec.rb @@ -1,46 +1,44 @@ require 'spec_helper' describe OrderCyclesHelper do + let(:oc) { double(:order_cycle) } + describe "finding producer enterprise options" do before do - helper.stub(:order_cycle_producer_enterprises) { "enterprise list" } + helper.stub(:permitted_producer_enterprises_for) { "enterprise list" } end it "asks for a validation option list" do expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true}) - helper.order_cycle_producer_enterprise_options + helper.permitted_producer_enterprise_options_for(oc) end end describe "finding coodinator enterprise options" do before do - helper.stub(:order_cycle_coordinating_enterprises) { "enterprise list" } + helper.stub(:permitted_coordinating_enterprises_for) { "enterprise list" } end it "asks for a validation option list" do expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true}) - helper.order_cycle_coordinating_enterprise_options + helper.permitted_coordinating_enterprise_options_for(oc) end end describe "finding hub enterprise options" do before do - helper.stub(:order_cycle_hub_enterprises) { "enterprise list" } + helper.stub(:permitted_hub_enterprises_for) { "enterprise list" } end it "asks for a validation option list" do expect(helper).to receive(:validated_enterprise_options).with("enterprise list", {confirmed: true, shipping_and_payment_methods: true}) - helper.order_cycle_hub_enterprise_options + helper.permitted_hub_enterprise_options_for(oc) end end describe "building a validated enterprise list" do let(:e) { create(:distributor_enterprise, name: 'enterprise') } - before do - helper.stub(:order_cycle_permitted_enterprises) { Enterprise.where(id: e.id) } - end - it "returns enterprises without shipping methods as disabled" do create(:payment_method, distributors: [e]) expect(helper.send(:validated_enterprise_options, [e], shipping_and_payment_methods: true)) From 524645aff44d9b3795890a593620cc107ad9dc3e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 25 Mar 2015 11:29:35 +1100 Subject: [PATCH 267/384] Replacing visible_variants_for(exchange) with more general methods for determining visbility based on the enterprises involced and the current order cyclegs --- lib/open_food_network/permissions.rb | 60 ++++++++++++------- .../lib/open_food_network/permissions_spec.rb | 50 ++++++++++++---- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 6d0a802c30..391f016983 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -123,33 +123,47 @@ module OpenFoodNetwork Exchange.where(id: ids, order_cycle_id: order_cycle) end - # Find the variants within an exchange that a user can POTENTIALLY see + # Find the variants that a user can POTENTIALLY see within incoming exchanges + def visible_variants_for_incoming_exchanges_between(producer, coordinator) + if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) + # All variants belonging to the producer + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + Spree::Variant.where("1=0") # None + end + end + + # Find the variants that a user is permitted see within outgoing exchanges # Note that this does not determine whether they actually appear in outgoing exchanges # as this requires first that the variant is included in an incoming exchange - def visible_variants_within(exchange) - if exchange.incoming - if managed_enterprises.pluck(:id).include?(exchange.receiver_id) || managed_enterprises.pluck(:id).include?(exchange.sender_id) - # All variants belonging to the producer - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', exchange.sender_id) - else - [] # None - end - else - if managed_enterprises.pluck(:id).include?(exchange.receiver_id) || managed_enterprises.pluck(:id).include?(exchange.sender_id) - # Any variants of any producers that have granted the receiver P-OC - producers = granting(:add_to_order_cycle, to: [exchange.receiver], scope: Enterprise.is_primary_producer) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - # PLUS any variants that are already in the exchange, so things don't break + def visible_variants_for_outgoing_exchanges_between(coordinator, hub, options={}) + return Spree::Variant.where("1=0") unless options[:order_cycle] + if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(coordinator.id) + # Any variants of any producers that have granted the hub P-OC + producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = [] + options[:order_cycle].exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| active_variants = exchange.variants - Spree::Variant.where(id: permitted_variants | active_variants) - else - # Any variants produced by MY PRODUCERS, where my producer has granted P-OC to the receiver - producers = granting(:add_to_order_cycle, to: [exchange.receiver], scope: managed_enterprises.is_primary_producer) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - # PLUS any of my producers variants that are already in the exchange, so things don't break - active_variants = exchange.variants.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) - Spree::Variant.where(id: permitted_variants | active_variants) end + + Spree::Variant.where(id: permitted_variants | active_variants) + else + # Any variants produced by MY PRODUCERS, where my producer has granted P-OC to the hub + producers = granting(:add_to_order_cycle, to: [hub], scope: managed_enterprises.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any of my producers variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = [] + options[:order_cycle].exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| + active_variants = exchange.variants.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + end + + Spree::Variant.where(id: permitted_variants | active_variants) end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 185ec26d97..7874323924 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -318,7 +318,7 @@ module OpenFoodNetwork end end - describe "finding the variants within a given exchange which are visible to a user" do + describe "finding the variants within a hypothetical exchange between two enterprises which are visible to a user" do let!(:producer1) { create(:supplier_enterprise) } let!(:producer2) { create(:supplier_enterprise) } let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } @@ -326,15 +326,13 @@ module OpenFoodNetwork let(:oc) { create(:simple_order_cycle) } describe "incoming exchanges" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: e1, incoming: true) } - context "as a manager of the coordinator" do before do permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } end it "returns all variants belonging to the sending producer" do - visible = permissions.visible_variants_within(ex) + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1) expect(visible).to include v1 expect(visible).to_not include v2 end @@ -346,7 +344,7 @@ module OpenFoodNetwork end it "returns all variants belonging to the sending producer" do - visible = permissions.visible_variants_within(ex) + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1) expect(visible).to include v1 expect(visible).to_not include v2 end @@ -359,15 +357,13 @@ module OpenFoodNetwork end it "returns no variants" do - visible = permissions.visible_variants_within(ex) + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1) expect(visible).to eq [] end end end describe "outgoing exchanges" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - context "as a manager of the coordinator" do before do permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } @@ -375,10 +371,23 @@ module OpenFoodNetwork end it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.visible_variants_within(ex) + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) expect(visible).to include v1 expect(visible).to_not include v2 end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to e2 (or e1 for that matter) + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1, v2 + end + end end context "as manager of the outgoing hub" do @@ -388,10 +397,23 @@ module OpenFoodNetwork end it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.visible_variants_within(ex) + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) expect(visible).to include v1 expect(visible).to_not include v2 end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to e2 + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1, v2 + end + end end context "as the manager of a producer which has granted P-OC to the outgoing hub" do @@ -401,7 +423,7 @@ module OpenFoodNetwork end it "returns all of my produced variants" do - visible = permissions.visible_variants_within(ex) + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) expect(visible).to include v1 expect(visible).to_not include v2 end @@ -414,12 +436,14 @@ module OpenFoodNetwork end it "returns an empty array" do - expect(permissions.visible_variants_within(ex)).to eq [] + expect(permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc)).to eq [] end end # TODO: for backwards compatability, remove later context "as the manager of a producer which has not granted P-OC to the outgoing hub, but which has variants already in the exchange" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + # This one won't be in the exchange, and so shouldn't be visible let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } @@ -430,7 +454,7 @@ module OpenFoodNetwork end it "returns those variants that are in the exchange" do - visible = permissions.visible_variants_within(ex) + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) expect(visible).to_not include v1, v3 expect(visible).to include v2 end From 63e345f819e992e5d9eed451297822472715d67d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 25 Mar 2015 11:58:07 +1100 Subject: [PATCH 268/384] Exchange serializer cuts down list of variants visible to the current user, based on permissions --- .../api/admin/exchange_serializer.rb | 10 +++++++- .../admin/exchange_serializer_spec.rb | 25 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 spec/serializers/admin/exchange_serializer_spec.rb diff --git a/app/serializers/api/admin/exchange_serializer.rb b/app/serializers/api/admin/exchange_serializer.rb index c9fcad75db..bd143534e7 100644 --- a/app/serializers/api/admin/exchange_serializer.rb +++ b/app/serializers/api/admin/exchange_serializer.rb @@ -4,6 +4,14 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer has_many :enterprise_fees, serializer: Api::Admin::EnterpriseFeeSerializer def variants - Hash[ object.variants.map { |v| [v.id, true] } ] + permitted = Spree::Variant.where("1=0") + if object.incoming + permitted = OpenFoodNetwork::Permissions.new(options[:current_user]). + visible_variants_for_incoming_exchanges_between(object.sender, object.receiver) + else + permitted = OpenFoodNetwork::Permissions.new(options[:current_user]). + visible_variants_for_outgoing_exchanges_between(object.sender, object.receiver, order_cycle: object.order_cycle) + end + Hash[ object.variants.merge(permitted).map { |v| [v.id, true] } ] end end diff --git a/spec/serializers/admin/exchange_serializer_spec.rb b/spec/serializers/admin/exchange_serializer_spec.rb new file mode 100644 index 0000000000..9b32c537b3 --- /dev/null +++ b/spec/serializers/admin/exchange_serializer_spec.rb @@ -0,0 +1,25 @@ +describe Api::Admin::ExchangeSerializer do + let(:v1) { create(:variant) } + let(:v2) { create(:variant) } + let(:exchange) { create(:exchange, incoming: false, variants: [v1, v2]) } + let(:permissions_mock) { double(:permissions) } + let(:serializer) { Api::Admin::ExchangeSerializer.new exchange } + + + before do + allow(OpenFoodNetwork::Permissions).to receive(:new) { permissions_mock } + allow(permissions_mock).to receive(:visible_variants_for_outgoing_exchanges_between) do + # This is the permitted list of variants + Spree::Variant.where(id: [v1] ) + end + end + + it "filters variants within the exchange based on permissions" do + visible_variants = serializer.variants + expect(permissions_mock).to have_received(:visible_variants_for_outgoing_exchanges_between). + with(exchange.sender, exchange.receiver, order_cycle: exchange.order_cycle) + expect(exchange.variants).to include v1, v2 + expect(visible_variants.keys).to include v1.id + expect(visible_variants.keys).to_not include v2.id + end +end From 99bd6244ed09fc4a6fe6f9464288e7b7bfdf9272 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 25 Mar 2015 15:38:08 +1100 Subject: [PATCH 269/384] Filtering the list of variants visible within each exchange based on permissions --- .../filters/visible_products.js.coffee | 3 +++ .../filters/visible_variants.js.coffee | 4 ++++ .../api/admin/order_cycle_serializer.rb | 14 ++++++++++++++ .../_exchange_distributed_products_form.html.haml | 5 ++++- .../_exchange_supplied_products_form.html.haml | 2 ++ 5 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee create mode 100644 app/assets/javascripts/admin/order_cycles/filters/visible_variants.js.coffee diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee new file mode 100644 index 0000000000..2684fe1287 --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee @@ -0,0 +1,3 @@ +angular.module("admin.order_cycles").filter "visibleProducts", ($filter) -> + return (products, exchange, rules) -> + return (product for product in products when $filter('visibleVariants')(product, exchange, rules).length > 0) diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_variants.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_variants.js.coffee new file mode 100644 index 0000000000..feb1635cce --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/filters/visible_variants.js.coffee @@ -0,0 +1,4 @@ +angular.module("admin.order_cycles").filter "visibleVariants", -> + return (product, exchange, rules) -> + enterprise_id = if exchange.incoming then exchange.sender_id else exchange.receiver_id + return (variant for variant in product.variants when variant.id in rules[exchange.enterprise_id]) diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index a4c129774e..333cdfacdc 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -1,5 +1,6 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges + attributes :visible_variants_for_outgoing_exchanges has_many :coordinator_fees, serializer: Api::IdSerializer @@ -15,4 +16,17 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer scoped_exchanges = OpenFoodNetwork::Permissions.new(options[:current_user]).order_cycle_exchanges(object).order('id ASC') ActiveModel::ArraySerializer.new(scoped_exchanges, {each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user] }) end + + def visible_variants_for_outgoing_exchanges + # For each enterprise that the current user is able to see in this order cycle, + # work out which variants should be visible within outgoing exchanges from that enterprise + visible = {} + enterprises = OpenFoodNetwork::Permissions.new(options[:current_user]).order_cycle_enterprises_for(order_cycle: object) + enterprises.each do |enterprise| + variants = OpenFoodNetwork::Permissions.new(options[:current_user]). + visible_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) + visible[enterprise.id] = variants if variants.any? + end + visible + end end diff --git a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml index 6566ee770a..efdf3bdf35 100644 --- a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml @@ -4,13 +4,16 @@ = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, incomingExchangesVariants(), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' Select all - .exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle'} + -# Scope product list based on permissions the current user has to view variants in this exchange + .exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges' } .exchange-product-details .supplier {{ product.supplier_name }} %label = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-disabled' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' %img{'ng-src' => '{{ product.image_url }}'} {{ product.name }} + + -# if we ever need to filter variants within a product using visibility permissions, we can use this filter: visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges .exchange-product-variant{'ng-repeat' => 'variant in product.variants | filter:variantSuppliedToOrderCycle'} %label = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}' diff --git a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml index 4e4ae4c3f3..a23c0bc4ba 100644 --- a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml @@ -5,6 +5,8 @@ = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants' Select all + -# No need to scope product list based on permissions, because if an incoming exchange is visible, + -# then all of the variants within it should be visible. May change in the future? .exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'} .exchange-product-details From 681dada5dfaad9b649074497f6d9ca2ff4f0322c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 26 Mar 2015 22:21:06 +1100 Subject: [PATCH 270/384] Simple OC form loads coordinator into JS --- .../admin/order_cycles/controllers/simple_create.js.coffee | 4 ++-- .../admin/order_cycles/controllers/simple_edit.js.coffee | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee index d8281ed1d4..42b3b69fdc 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee @@ -1,5 +1,5 @@ -angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, OrderCycle, Enterprise, EnterpriseFee) -> - $scope.enterprises = Enterprise.index {}, (enterprises) => +angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> + $scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) => $scope.init(enterprises) $scope.enterprise_fees = EnterpriseFee.index() $scope.order_cycle = OrderCycle.order_cycle diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee index 84f2fcf74a..fdc15e220c 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee @@ -2,7 +2,7 @@ angular.module('admin.order_cycles').controller "AdminSimpleEditOrderCycleCtrl", $scope.orderCycleId = -> $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] - $scope.enterprises = Enterprise.index() + $scope.enterprises = Enterprise.index(order_cycle_id: $scope.orderCycleId()) $scope.enterprise_fees = EnterpriseFee.index() $scope.order_cycle = OrderCycle.load $scope.orderCycleId(), (order_cycle) => $scope.init() From 5e015f061114526d3228a52cd0748d15130d54a5 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 26 Mar 2015 22:24:37 +1100 Subject: [PATCH 271/384] Rename visibleVariants filter to visibleProductVariants --- .../order_cycles/filters/visible_product_variants.js.coffee | 4 ++++ .../admin/order_cycles/filters/visible_products.js.coffee | 2 +- .../admin/order_cycles/filters/visible_variants.js.coffee | 4 ---- 3 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/admin/order_cycles/filters/visible_product_variants.js.coffee delete mode 100644 app/assets/javascripts/admin/order_cycles/filters/visible_variants.js.coffee diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_product_variants.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_product_variants.js.coffee new file mode 100644 index 0000000000..8989af5b9d --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/filters/visible_product_variants.js.coffee @@ -0,0 +1,4 @@ +angular.module("admin.order_cycles").filter "visibleProductVariants", -> + return (product, exchange, rules) -> + variants = product.variants.concat( [{ "id": product.master_id}] ) + return (variant for variant in variants when variant.id in rules[exchange.enterprise_id]) diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee index 2684fe1287..3afff183a1 100644 --- a/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee @@ -1,3 +1,3 @@ angular.module("admin.order_cycles").filter "visibleProducts", ($filter) -> return (products, exchange, rules) -> - return (product for product in products when $filter('visibleVariants')(product, exchange, rules).length > 0) + return (product for product in products when $filter('visibleProductVariants')(product, exchange, rules).length > 0) diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_variants.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_variants.js.coffee deleted file mode 100644 index feb1635cce..0000000000 --- a/app/assets/javascripts/admin/order_cycles/filters/visible_variants.js.coffee +++ /dev/null @@ -1,4 +0,0 @@ -angular.module("admin.order_cycles").filter "visibleVariants", -> - return (product, exchange, rules) -> - enterprise_id = if exchange.incoming then exchange.sender_id else exchange.receiver_id - return (variant for variant in product.variants when variant.id in rules[exchange.enterprise_id]) From 540ac845da575299f325b65bd2a2557354b10d52 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 26 Mar 2015 22:26:48 +1100 Subject: [PATCH 272/384] Fixing broken js spec --- .../admin/order_cycles/controllers/simple_create.js.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee index bf2498e234..d2111ca010 100644 --- a/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee +++ b/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee @@ -21,10 +21,11 @@ describe "AdminSimpleCreateOrderCycleCtrl", -> suppliedVariants: jasmine.createSpy().andReturn('supplied variants') EnterpriseFee = index: jasmine.createSpy() + ocInstance = {} module('admin.order_cycles') inject ($controller) -> - ctrl = $controller 'AdminSimpleCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee} + ctrl = $controller 'AdminSimpleCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee, ocInstance: ocInstance} describe "initialisation", -> enterprise = {id: 123} @@ -46,4 +47,4 @@ describe "AdminSimpleCreateOrderCycleCtrl", -> toHaveBeenCalledWith(incoming_exchange, 'supplied variants', true) it "sets the coordinator", -> - expect(OrderCycle.order_cycle.coordinator_id).toEqual enterprise.id \ No newline at end of file + expect(OrderCycle.order_cycle.coordinator_id).toEqual enterprise.id From 193a061c445dd6e8210cf839f1b75baaeaf9e6d1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 26 Mar 2015 22:29:30 +1100 Subject: [PATCH 273/384] Order cycle form applicator only updates visible variants, should add an 'editable variants' scope to permissions --- .../admin/order_cycles_controller.rb | 4 +- .../order_cycle_form_applicator.rb | 67 ++++++++- .../order_cycle_form_applicator_spec.rb | 132 +++++++++++++++--- 3 files changed, 174 insertions(+), 29 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index efb32f2483..e348dcdcf6 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -31,7 +31,7 @@ module Admin respond_to do |format| if @order_cycle.save - OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, permitted_enterprises_for(@order_cycle)).go! + OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user, permitted_enterprises_for(@order_cycle)).go! flash[:notice] = 'Your order cycle has been created.' format.html { redirect_to admin_order_cycles_path } @@ -48,7 +48,7 @@ module Admin respond_to do |format| if @order_cycle.update_attributes(params[:order_cycle]) - OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, permitted_enterprises_for(@order_cycle)).go! + OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user, permitted_enterprises_for(@order_cycle)).go! flash[:notice] = 'Your order cycle has been updated.' format.html { redirect_to admin_order_cycles_path } diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index 6556223dac..3b8fce4aef 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -6,8 +6,9 @@ module OpenFoodNetwork # as much as possible (if not all) of its logic into Angular. class OrderCycleFormApplicator # The applicator will only touch exchanges where a permitted enterprise is the participant - def initialize(order_cycle, permitted_enterprises) + def initialize(order_cycle, spree_current_user, permitted_enterprises) @order_cycle = order_cycle + @spree_current_user = spree_current_user @permitted_enterprises = permitted_enterprises end @@ -16,7 +17,7 @@ module OpenFoodNetwork @order_cycle.incoming_exchanges ||= [] @order_cycle.incoming_exchanges.each do |exchange| - variant_ids = exchange_variant_ids(exchange) + variant_ids = incoming_exchange_variant_ids(exchange) enterprise_fee_ids = exchange[:enterprise_fee_ids] if exchange_exists?(exchange[:enterprise_id], @order_cycle.coordinator_id, true) @@ -30,7 +31,7 @@ module OpenFoodNetwork @order_cycle.outgoing_exchanges ||= [] @order_cycle.outgoing_exchanges.each do |exchange| - variant_ids = exchange_variant_ids(exchange) + variant_ids = outgoing_exchange_variant_ids(exchange) enterprise_fee_ids = exchange[:enterprise_fee_ids] if exchange_exists?(@order_cycle.coordinator_id, exchange[:enterprise_id], false) @@ -92,9 +93,65 @@ module OpenFoodNetwork @permitted_enterprises.include? exchange.participant end + # TODO Need to use editable rather than visible + def editable_variant_ids_for_incoming_exchange_between(sender, receiver) + OpenFoodNetwork::Permissions.new(@spree_current_user). + visible_variants_for_incoming_exchanges_between(sender, receiver). + pluck(:id) + end - def exchange_variant_ids(exchange) - exchange[:variants].select { |k, v| v }.keys.map { |k| k.to_i } + # TODO Need to use editable rather than visible + def editable_variant_ids_for_outgoing_exchange_between(sender, receiver) + OpenFoodNetwork::Permissions.new(@spree_current_user). + visible_variants_for_outgoing_exchanges_between(sender, receiver, order_cycle: @order_cycle). + pluck(:id) + end + + def find_incoming_exchange(attrs) + @order_cycle.exchanges. + where(:sender_id => attrs[:enterprise_id], :receiver_id => @order_cycle.coordinator_id, :incoming => true).first + end + + def find_outgoing_exchange(attrs) + @order_cycle.exchanges. + where(:sender_id => @order_cycle.coordinator_id, :receiver_id => attrs[:enterprise_id], :incoming => false).first + end + + def persisted_variants_hash(exchange) + exchange ||= OpenStruct.new(variants: []) + Hash[ exchange.variants.map{ |v| [v.id, true] } ] + end + + def incoming_exchange_variant_ids(attrs) + exchange = find_incoming_exchange(attrs) + variants = persisted_variants_hash(exchange) + + sender = exchange.andand.sender || Enterprise.find(attrs[:enterprise_id]) + receiver = @order_cycle.coordinator + permitted = editable_variant_ids_for_incoming_exchange_between(sender, receiver) + + # Only change visibility for variants I have permission to edit + attrs[:variants].each do |variant_id, value| + variants[variant_id.to_i] = value if permitted.include?(variant_id.to_i) + end + + variants.select { |k, v| v }.keys.map { |k| k.to_i }.sort + end + + def outgoing_exchange_variant_ids(attrs) + exchange = find_outgoing_exchange(attrs) + variants = persisted_variants_hash(exchange) + + sender = @order_cycle.coordinator + receiver = exchange.andand.receiver || Enterprise.find(attrs[:enterprise_id]) + permitted = editable_variant_ids_for_outgoing_exchange_between(sender, receiver) + + # Only change visibility for variants I have permission to edit + attrs[:variants].each do |variant_id, value| + variants[variant_id.to_i] = value if permitted.include?(variant_id.to_i) + end + + variants.select { |k, v| v }.keys.map { |k| k.to_i }.sort end end end diff --git a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb index c63d1eac25..895c2c6246 100644 --- a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb +++ b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb @@ -2,6 +2,10 @@ require 'open_food_network/order_cycle_form_applicator' module OpenFoodNetwork describe OrderCycleFormApplicator do + include AuthenticationWorkflow + + let!(:user) { create_enterprise_user } + context "unit specs" do it "creates new exchanges for incoming_exchanges" do coordinator_id = 123 @@ -11,9 +15,9 @@ module OpenFoodNetwork oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => []) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) - applicator.should_receive(:exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) + applicator.should_receive(:incoming_exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id, true).and_return(false) applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id, true, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) applicator.should_receive(:destroy_untouched_exchanges) @@ -29,9 +33,9 @@ module OpenFoodNetwork oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange]) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) - applicator.should_receive(:exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) + applicator.should_receive(:outgoing_exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id, false).and_return(false) applicator.should_receive(:add_exchange).with(coordinator_id, distributor_id, false, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) applicator.should_receive(:destroy_untouched_exchanges) @@ -51,9 +55,9 @@ module OpenFoodNetwork :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => []) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) - applicator.should_receive(:exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) + applicator.should_receive(:incoming_exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id, true).and_return(true) applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id, true, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) applicator.should_receive(:destroy_untouched_exchanges) @@ -73,9 +77,9 @@ module OpenFoodNetwork :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange]) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) - applicator.should_receive(:exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) + applicator.should_receive(:outgoing_exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id, false).and_return(true) applicator.should_receive(:update_exchange).with(coordinator_id, distributor_id, false, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) applicator.should_receive(:destroy_untouched_exchanges) @@ -95,7 +99,7 @@ module OpenFoodNetwork :incoming_exchanges => [], :outgoing_exchanges => []) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) applicator.should_receive(:destroy_untouched_exchanges) @@ -108,7 +112,7 @@ module OpenFoodNetwork e2 = double(:exchange2, id: 1, foo: 2) oc = double(:order_cycle, :exchanges => [e1]) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) applicator.instance_eval do @touched_exchanges = [e2] end @@ -117,7 +121,7 @@ module OpenFoodNetwork end it "does not destroy exchanges involving enterprises it does not have permission to touch" do - applicator = OrderCycleFormApplicator.new(nil, []) + applicator = OrderCycleFormApplicator.new(nil, user, []) exchanges = double(:exchanges) permitted_exchanges = [double(:exchange), double(:exchange)] @@ -129,10 +133,94 @@ module OpenFoodNetwork end end - it "converts exchange variant ids hash to an array of ids" do - applicator = OrderCycleFormApplicator.new(nil, []) + describe "finding alterable exchange variants" do + let(:coordinator_mock) { double(:enterprise) } + let(:oc) { double(:oc, coordinator: coordinator_mock ) } + let!(:applicator) { OrderCycleFormApplicator.new(oc, user, []) } - applicator.send(:exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}).should == [1, 3] + describe "converting the existing variants of an exchange to a hash" do + context "when nil is passed in" do + let(:hash) { applicator.send(:persisted_variants_hash, nil) } + + it "returns an empty hash" do + expect(hash).to eq({}) + end + end + + context "when an exchange is passed in" do + let(:v1) { create(:variant) } + let(:exchange) { create(:exchange, variants: [v1]) } + let(:hash) { applicator.send(:persisted_variants_hash, exchange) } + + it "returns a hash with variant ids as keys an all values set to true" do + expect(hash.length).to be 1 + expect(hash[v1.id]).to be true + end + end + end + + context "where a matching exchange does not exist" do + let(:enterprise_mock) { double(:enterprise) } + + before do + applicator.stub(:find_outgoing_exchange) { nil } + expect(applicator).to receive(:editable_variant_ids_for_outgoing_exchange_between). + with(coordinator_mock, enterprise_mock) { [1, 2, 3] } + end + + it "converts exchange variant ids hash to an array of ids" do + applicator.stub(:persisted_variants_hash) { {} } + expect(Enterprise).to receive(:find) { enterprise_mock } + ids = applicator.send(:outgoing_exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}) + expect(ids).to eq [1, 3] + end + + it "restricts exchange variant ids to those editable by the current user" do + applicator.stub(:persisted_variants_hash) { {4 => true} } + expect(Enterprise).to receive(:find) { enterprise_mock } + ids = applicator.send(:outgoing_exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true, '4' => false}}) + expect(ids).to eq [1, 3, 4] + end + + it "overrides existing variants based on submitted data, when user has permission" do + applicator.stub(:persisted_variants_hash) { {2 => true} } + expect(Enterprise).to receive(:find) { enterprise_mock} + ids = applicator.send(:outgoing_exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}) + expect(ids).to eq [1, 3] + end + end + + context "where a matching exchange exists" do + let(:enterprise_mock) { double(:enterprise) } + let(:exchange_mock) { double(:exchange) } + + before do + applicator.stub(:find_outgoing_exchange) { exchange_mock } + expect(applicator).to receive(:editable_variant_ids_for_outgoing_exchange_between). + with(coordinator_mock, enterprise_mock) { [1, 2, 3] } + end + + it "converts exchange variant ids hash to an array of ids" do + applicator.stub(:persisted_variants_hash) { {} } + expect(exchange_mock).to receive(:receiver) { enterprise_mock } + ids = applicator.send(:outgoing_exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}) + expect(ids).to eq [1, 3] + end + + it "restricts exchange variant ids to those editable by the current user" do + applicator.stub(:persisted_variants_hash) { {4 => true} } + expect(exchange_mock).to receive(:receiver) { enterprise_mock } + ids = applicator.send(:outgoing_exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true, '4' => false}}) + expect(ids).to eq [1, 3, 4] + end + + it "overrides existing variants based on submitted data, when user has permission" do + applicator.stub(:persisted_variants_hash) { {2 => true} } + expect(exchange_mock).to receive(:receiver) { enterprise_mock } + ids = applicator.send(:outgoing_exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}) + expect(ids).to eq [1, 3] + end + end end describe "filtering exchanges for permission" do @@ -141,7 +229,7 @@ module OpenFoodNetwork e = double(:enterprise) ex = double(:exchange, participant: e) - applicator = OrderCycleFormApplicator.new(nil, [e]) + applicator = OrderCycleFormApplicator.new(nil, user, [e]) applicator.send(:permission_for, ex).should be_true end @@ -149,7 +237,7 @@ module OpenFoodNetwork e = double(:enterprise) ex = double(:exchange, participant: e) - applicator = OrderCycleFormApplicator.new(nil, []) + applicator = OrderCycleFormApplicator.new(nil, user, []) applicator.send(:permission_for, ex).should be_false end end @@ -157,7 +245,7 @@ module OpenFoodNetwork describe "filtering many exchanges" do it "returns exchanges involving enterprises we have permission to touch" do ex1, ex2 = double(:exchange), double(:exchange) - applicator = OrderCycleFormApplicator.new(nil, []) + applicator = OrderCycleFormApplicator.new(nil, user, []) applicator.stub(:permission_for).and_return(true, false) applicator.send(:with_permission, [ex1, ex2]).should == [ex1] end @@ -173,7 +261,7 @@ module OpenFoodNetwork it "checks whether exchanges exist" do oc = FactoryGirl.create(:simple_order_cycle) exchange = FactoryGirl.create(:exchange, order_cycle: oc) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id, exchange.incoming).should be_true applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id, !exchange.incoming).should be_false @@ -187,7 +275,7 @@ module OpenFoodNetwork sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, [sender, receiver]) + applicator = OrderCycleFormApplicator.new(oc, user, [sender, receiver]) incoming = true variant1 = FactoryGirl.create(:variant) variant2 = FactoryGirl.create(:variant) @@ -211,7 +299,7 @@ module OpenFoodNetwork sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, [sender, receiver]) + applicator = OrderCycleFormApplicator.new(oc, user, [sender, receiver]) incoming = true variant1 = FactoryGirl.create(:variant) @@ -236,7 +324,7 @@ module OpenFoodNetwork sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) incoming = true expect do @@ -249,7 +337,7 @@ module OpenFoodNetwork sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, []) + applicator = OrderCycleFormApplicator.new(oc, user, []) incoming = true exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, incoming: incoming) variant1 = FactoryGirl.create(:variant) From 94309995407477d4b0050c31cd33230a2f66ba62 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 26 Mar 2015 22:38:53 +1100 Subject: [PATCH 274/384] Filtering exchange variants, for outgoing exchange variant count --- .../order_cycles/filters/visible_exchange_variants.js.coffee | 3 +++ app/views/admin/order_cycles/_exchange_form.html.haml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/admin/order_cycles/filters/visible_exchange_variants.js.coffee diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_exchange_variants.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_exchange_variants.js.coffee new file mode 100644 index 0000000000..b898df5371 --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/filters/visible_exchange_variants.js.coffee @@ -0,0 +1,3 @@ +angular.module("admin.order_cycles").filter "visibleExchangeVariants", -> + return (variants, exchange, rules) -> + return (variant for variant in variants when variant in rules[exchange.enterprise_id]) diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index 8f81b9f03f..cdd6bfbcd5 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -5,7 +5,7 @@ - if type == 'supplier' {{ enterpriseTotalVariants(enterprises[exchange.enterprise_id]) }} - else - {{ incomingExchangesVariants().length }} + {{ (incomingExchangesVariants() | visibleExchangeVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges).length }} selected - if type == 'distributor' %td.collection-details From 887579ecb86abe64fa852b5903c4c56060f8a679 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 26 Mar 2015 22:41:10 +1100 Subject: [PATCH 275/384] Use managed_by scope on Enterprise instead of user.enterprises --- app/views/admin/order_cycles/_form.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 489055459b..6d24e149f8 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -15,14 +15,14 @@ %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_supplied_products_form' -- if spree_current_user.enterprises.include? @order_cycle.coordinator +- if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator = select_tag :new_supplier_id, options_for_select(permitted_producer_enterprise_options_for(@order_cycle)), {'ng-model' => 'new_supplier_id'} = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' %h2 Coordinator --if spree_current_user.enterprises.include? @order_cycle.coordinator +-if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator = f.label :coordinator_id, 'Coordinator' = f.select :coordinator_id, permitted_coordinating_enterprise_options_for(@order_cycle), { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} = render 'coordinator_fees', f: f @@ -44,7 +44,7 @@ %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_distributed_products_form' -- if spree_current_user.enterprises.include? @order_cycle.coordinator +- if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator = select_tag :new_distributor_id, options_for_select(permitted_hub_enterprise_options_for(@order_cycle)), {'ng-model' => 'new_distributor_id'} = f.submit 'Add distributor', 'ng-click' => 'addDistributor($event)' From 43d2eb4d2280abe0844595b64c4130351f6c94e4 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Mar 2015 10:32:06 +1100 Subject: [PATCH 276/384] Adding 'new' method to admin order cycle service, for loading data from serializer for new OCs --- .../admin/order_cycle.js.erb.coffee | 3 +- .../services/order_cycle.js.coffee | 22 ++++++-- .../admin/order_cycles_controller.rb | 4 +- .../unit/order_cycle_spec.js.coffee | 50 ++++++++++++++++--- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 78db9232dc..21acdc108d 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -4,8 +4,7 @@ angular.module('admin.order_cycles', ['ngResource']) $scope.supplied_products = Enterprise.supplied_products $scope.enterprise_fees = EnterpriseFee.index() - $scope.order_cycle = OrderCycle.order_cycle - $scope.order_cycle.coordinator_id = ocInstance.coordinator_id + $scope.order_cycle = OrderCycle.new({ coordinator_id: ocInstance.coordinator_id}) $scope.loaded = -> Enterprise.loaded && EnterpriseFee.loaded diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee index 40197452bf..2aeec33d1f 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee @@ -1,14 +1,12 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) -> - OrderCycle = $resource '/admin/order_cycles/:order_cycle_id.json', {}, { + OrderCycle = $resource '/admin/order_cycles/:action_name/:order_cycle_id.json', {}, { 'index': { method: 'GET', isArray: true} + 'new' : { method: 'GET', params: { action_name: "new" } } 'create': { method: 'POST'} 'update': { method: 'PUT'}} { - order_cycle: - incoming_exchanges: [] - outgoing_exchanges: [] - coordinator_fees: [] + order_cycle: {} loaded: false @@ -84,6 +82,20 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) for exchange in this.order_cycle.outgoing_exchanges exchange.variants[variant_id] = false + new: (params, callback=null) -> + OrderCycle.new params, (oc) => + delete oc.$promise + delete oc.$resolved + angular.extend(@order_cycle, oc) + @order_cycle.incoming_exchanges = [] + @order_cycle.outgoing_exchanges = [] + delete(@order_cycle.exchanges) + @loaded = true + + (callback || angular.noop)(@order_cycle) + + @order_cycle + load: (order_cycle_id, callback=null) -> service = this OrderCycle.get {order_cycle_id: order_cycle_id}, (oc) -> diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index e348dcdcf6..1f8ce9e738 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -22,7 +22,9 @@ module Admin def new respond_to do |format| format.html - format.json + format.json do + render json: Api::Admin::OrderCycleSerializer.new(@order_cycle, current_user: spree_current_user).to_json + end end end diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 43e474a49d..d294475acb 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -13,7 +13,6 @@ describe 'OrderCycle controllers', -> event = preventDefault: jasmine.createSpy('preventDefault') OrderCycle = - order_cycle: 'my order cycle' exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').andReturn('variants selected') productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').andReturn('product supplied') variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').andReturn('variant supplied') @@ -29,6 +28,7 @@ describe 'OrderCycle controllers', -> removeExchangeFee: jasmine.createSpy('removeExchangeFee') removeDistributionOfVariant: jasmine.createSpy('removeDistributionOfVariant') create: jasmine.createSpy('create') + new: jasmine.createSpy('new').andReturn "my order cycle" Enterprise = index: jasmine.createSpy('index').andReturn('enterprises list') supplied_products: 'supplied products' @@ -450,12 +450,15 @@ describe 'OrderCycle services', -> {sender_id: 1, receiver_id: 456, incoming: true} {sender_id: 456, receiver_id: 2, incoming: false} ] + $httpBackend.whenGET('/admin/order_cycles/new.json').respond + id: 123 + name: 'New Order Cycle' + coordinator_id: 456 + coordinator_fees: [] + exchanges: [] it 'initialises order cycle', -> - expect(OrderCycle.order_cycle).toEqual - incoming_exchanges: [] - outgoing_exchanges: [] - coordinator_fees: [] + expect(OrderCycle.order_cycle).toEqual {} it 'counts selected variants in an exchange', -> result = OrderCycle.exchangeSelectedVariants({variants: {1: true, 2: false, 3: true}}) @@ -503,6 +506,11 @@ describe 'OrderCycle services', -> describe 'adding suppliers', -> exchange = null + beforeEach -> + # Initialise OC + OrderCycle.new() + $httpBackend.flush() + it 'adds the supplier to incoming exchanges', -> OrderCycle.addSupplier('123') expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [ @@ -512,6 +520,11 @@ describe 'OrderCycle services', -> describe 'adding distributors', -> exchange = null + beforeEach -> + # Initialise OC + OrderCycle.new() + $httpBackend.flush() + it 'adds the distributor to outgoing exchanges', -> OrderCycle.addDistributor('123') expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ @@ -559,6 +572,9 @@ describe 'OrderCycle services', -> expect(OrderCycle.removeDistributionOfVariant).not.toHaveBeenCalled() it 'adds coordinator fees', -> + # Initialise OC + OrderCycle.new() + $httpBackend.flush() OrderCycle.addCoordinatorFee() expect(OrderCycle.order_cycle.coordinator_fees).toEqual [{}] @@ -680,7 +696,25 @@ describe 'OrderCycle services', -> $httpBackend.flush() expect(OrderCycle.loaded).toBe(true) - describe 'loading an order cycle', -> + describe 'loading a new order cycle', -> + beforeEach -> + OrderCycle.new() + $httpBackend.flush() + + + it 'loads basic fields', -> + expect(OrderCycle.order_cycle.id).toEqual(123) + expect(OrderCycle.order_cycle.name).toEqual('New Order Cycle') + expect(OrderCycle.order_cycle.coordinator_id).toEqual(456) + + it 'initialises the incoming and outgoing exchanges', -> + expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [] + expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [] + + it 'removes the original exchanges array', -> + expect(OrderCycle.order_cycle.exchanges).toBeUndefined() + + describe 'loading an existing order cycle', -> beforeEach -> OrderCycle.load('123') $httpBackend.flush() @@ -705,8 +739,8 @@ describe 'OrderCycle services', -> active: true ] - it 'removes original exchanges array', -> - expect(OrderCycle.order_cycle.exchanges).toEqual(undefined) + it 'removes the original exchanges array', -> + expect(OrderCycle.order_cycle.exchanges).toBeUndefined() describe 'creating an order cycle', -> it 'redirects to the order cycles page on success', -> From 4d14acb64d2e6d4462108d000385efcee60366c7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Mar 2015 10:39:13 +1100 Subject: [PATCH 277/384] Don't send non-attributes of js order_cycle object to server --- .../admin/order_cycles/services/order_cycle.js.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee index 2aeec33d1f..b93512c3bb 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee @@ -139,6 +139,7 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) dataForSubmit: -> data = this.deepCopy() + data = this.stripNonSubmittableAttributes(data) data = this.removeInactiveExchanges(data) data = this.translateCoordinatorFees(data) data = this.translateExchangeFees(data) @@ -159,6 +160,11 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) data + stripNonSubmittableAttributes: (order_cycle) -> + delete order_cycle.id + delete order_cycle.visible_variants_for_outgoing_exchanges + order_cycle + removeInactiveExchanges: (order_cycle) -> order_cycle.incoming_exchanges = (exchange for exchange in order_cycle.incoming_exchanges when exchange.active) From aa170ef5f6dc29d01ee3e2d3d583d3d89e280d9b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Mar 2015 11:29:02 +1100 Subject: [PATCH 278/384] Order cycle simple create controller uses 'new' method on OC service to initialise --- .../order_cycles/controllers/simple_create.js.coffee | 3 ++- .../order_cycles/controllers/simple_create.js.coffee | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee index 42b3b69fdc..cd61fffa1b 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee @@ -1,8 +1,9 @@ angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> + # TODO: make this a get method, which only fetches one enterprise $scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) => $scope.init(enterprises) $scope.enterprise_fees = EnterpriseFee.index() - $scope.order_cycle = OrderCycle.order_cycle + $scope.order_cycle = OrderCycle.new({coordinator_id: ocInstance.coordinator_id}) $scope.init = (enterprises) -> enterprise = enterprises[Object.keys(enterprises)[0]] diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee index d2111ca010..0f1e043467 100644 --- a/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee +++ b/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee @@ -9,14 +9,18 @@ describe "AdminSimpleCreateOrderCycleCtrl", -> beforeEach -> scope = {} + order_cycle = + coordinator_id: 123 + incoming_exchanges: [incoming_exchange] + outgoing_exchanges: [outgoing_exchange] OrderCycle = - order_cycle: - incoming_exchanges: [incoming_exchange] - outgoing_exchanges: [outgoing_exchange] + order_cycle: order_cycle addSupplier: jasmine.createSpy() addDistributor: jasmine.createSpy() setExchangeVariants: jasmine.createSpy() + new: jasmine.createSpy().andReturn order_cycle Enterprise = + get: jasmine.createSpy().andReturn {id: 123} index: jasmine.createSpy() suppliedVariants: jasmine.createSpy().andReturn('supplied variants') EnterpriseFee = From 3817ef202c7d6ad3b572307ebe87855b17881e3a Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Mar 2015 15:47:27 +1100 Subject: [PATCH 279/384] Enterprises in simple OC create controller only load after OC initialises --- .../order_cycles/controllers/simple_create.js.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee index cd61fffa1b..abc26271a1 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee @@ -1,9 +1,9 @@ angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> - # TODO: make this a get method, which only fetches one enterprise - $scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) => - $scope.init(enterprises) + $scope.order_cycle = OrderCycle.new {coordinator_id: ocInstance.coordinator_id}, => + # TODO: make this a get method, which only fetches one enterprise + $scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) => + $scope.init(enterprises) $scope.enterprise_fees = EnterpriseFee.index() - $scope.order_cycle = OrderCycle.new({coordinator_id: ocInstance.coordinator_id}) $scope.init = (enterprises) -> enterprise = enterprises[Object.keys(enterprises)[0]] From f5bacf71b7b200b3458a3a1b2a707a092a6c989b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Mar 2015 15:48:44 +1100 Subject: [PATCH 280/384] Permissions for OrderCycleFormApplicator are determined internally --- .../admin/order_cycles_controller.rb | 4 +- .../order_cycle_form_applicator.rb | 20 ++++++-- .../order_cycle_form_applicator_spec.rb | 48 ++++++++++++------- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 1f8ce9e738..c8d5af73e6 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -33,7 +33,7 @@ module Admin respond_to do |format| if @order_cycle.save - OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user, permitted_enterprises_for(@order_cycle)).go! + OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user).go! flash[:notice] = 'Your order cycle has been created.' format.html { redirect_to admin_order_cycles_path } @@ -50,7 +50,7 @@ module Admin respond_to do |format| if @order_cycle.update_attributes(params[:order_cycle]) - OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user, permitted_enterprises_for(@order_cycle)).go! + OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user).go! flash[:notice] = 'Your order cycle has been updated.' format.html { redirect_to admin_order_cycles_path } diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index 3b8fce4aef..b8ecd9fa84 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -6,10 +6,9 @@ module OpenFoodNetwork # as much as possible (if not all) of its logic into Angular. class OrderCycleFormApplicator # The applicator will only touch exchanges where a permitted enterprise is the participant - def initialize(order_cycle, spree_current_user, permitted_enterprises) + def initialize(order_cycle, spree_current_user) @order_cycle = order_cycle @spree_current_user = spree_current_user - @permitted_enterprises = permitted_enterprises end def go! @@ -77,7 +76,9 @@ module OpenFoodNetwork end def destroy_untouched_exchanges - with_permission(untouched_exchanges).each(&:destroy) + if user_manages_coordinator? + with_permission(untouched_exchanges).each(&:destroy) + end end def untouched_exchanges @@ -90,7 +91,18 @@ module OpenFoodNetwork end def permission_for(exchange) - @permitted_enterprises.include? exchange.participant + permitted_enterprises.include? exchange.participant + end + + def permitted_enterprises + return @permitted_enterprises unless @permitted_enterprises.nil? + @permitted_enterprises = OpenFoodNetwork::Permissions.new(@spree_current_user). + order_cycle_enterprises_for(order_cycle: @order_cycle) + end + + def user_manages_coordinator? + return @user_manages_coordinator unless @user_manages_coordinator.nil? + @user_manages_coordinator = Enterprise.managed_by(@spree_current_user).include? @order_cycle.coordinator end # TODO Need to use editable rather than visible diff --git a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb index 895c2c6246..e7761c6193 100644 --- a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb +++ b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb @@ -15,7 +15,7 @@ module OpenFoodNetwork oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => []) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) applicator.should_receive(:incoming_exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id, true).and_return(false) @@ -33,7 +33,7 @@ module OpenFoodNetwork oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange]) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) applicator.should_receive(:outgoing_exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id, false).and_return(false) @@ -55,7 +55,7 @@ module OpenFoodNetwork :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => []) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) applicator.should_receive(:incoming_exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id, true).and_return(true) @@ -77,7 +77,7 @@ module OpenFoodNetwork :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange]) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) applicator.should_receive(:outgoing_exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id, false).and_return(true) @@ -99,7 +99,7 @@ module OpenFoodNetwork :incoming_exchanges => [], :outgoing_exchanges => []) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) applicator.should_receive(:destroy_untouched_exchanges) @@ -112,7 +112,7 @@ module OpenFoodNetwork e2 = double(:exchange2, id: 1, foo: 2) oc = double(:order_cycle, :exchanges => [e1]) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) applicator.instance_eval do @touched_exchanges = [e2] end @@ -121,7 +121,8 @@ module OpenFoodNetwork end it "does not destroy exchanges involving enterprises it does not have permission to touch" do - applicator = OrderCycleFormApplicator.new(nil, user, []) + applicator = OrderCycleFormApplicator.new(nil, user) + applicator.stub(:user_manages_coordinator?) { true } exchanges = double(:exchanges) permitted_exchanges = [double(:exchange), double(:exchange)] @@ -131,12 +132,21 @@ module OpenFoodNetwork applicator.send(:destroy_untouched_exchanges) end + + it "does not destroy any exchanges when it is not the coordinator" do + applicator = OrderCycleFormApplicator.new(nil, user) + applicator.stub(:user_manages_coordinator?) { false } + + expect(applicator).to_not receive(:with_permission) + + applicator.send(:destroy_untouched_exchanges) + end end describe "finding alterable exchange variants" do let(:coordinator_mock) { double(:enterprise) } let(:oc) { double(:oc, coordinator: coordinator_mock ) } - let!(:applicator) { OrderCycleFormApplicator.new(oc, user, []) } + let!(:applicator) { OrderCycleFormApplicator.new(oc, user) } describe "converting the existing variants of an exchange to a hash" do context "when nil is passed in" do @@ -229,7 +239,9 @@ module OpenFoodNetwork e = double(:enterprise) ex = double(:exchange, participant: e) - applicator = OrderCycleFormApplicator.new(nil, user, [e]) + applicator = OrderCycleFormApplicator.new(nil, user) + applicator.stub(:permitted_enterprises) { [e] } + applicator.send(:permission_for, ex).should be_true end @@ -237,7 +249,9 @@ module OpenFoodNetwork e = double(:enterprise) ex = double(:exchange, participant: e) - applicator = OrderCycleFormApplicator.new(nil, user, []) + applicator = OrderCycleFormApplicator.new(nil, user) + applicator.stub(:permitted_enterprises) { [] } + applicator.send(:permission_for, ex).should be_false end end @@ -245,7 +259,7 @@ module OpenFoodNetwork describe "filtering many exchanges" do it "returns exchanges involving enterprises we have permission to touch" do ex1, ex2 = double(:exchange), double(:exchange) - applicator = OrderCycleFormApplicator.new(nil, user, []) + applicator = OrderCycleFormApplicator.new(nil, user) applicator.stub(:permission_for).and_return(true, false) applicator.send(:with_permission, [ex1, ex2]).should == [ex1] end @@ -261,7 +275,7 @@ module OpenFoodNetwork it "checks whether exchanges exist" do oc = FactoryGirl.create(:simple_order_cycle) exchange = FactoryGirl.create(:exchange, order_cycle: oc) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id, exchange.incoming).should be_true applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id, !exchange.incoming).should be_false @@ -275,7 +289,8 @@ module OpenFoodNetwork sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, user, [sender, receiver]) + applicator = OrderCycleFormApplicator.new(oc, user) + applicator.stub(:permitted_enterprises) { [sender, receiver] } incoming = true variant1 = FactoryGirl.create(:variant) variant2 = FactoryGirl.create(:variant) @@ -299,7 +314,8 @@ module OpenFoodNetwork sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, user, [sender, receiver]) + applicator = OrderCycleFormApplicator.new(oc, user) + applicator.stub(:permitted_enterprises) { [sender, receiver] } incoming = true variant1 = FactoryGirl.create(:variant) @@ -324,7 +340,7 @@ module OpenFoodNetwork sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) incoming = true expect do @@ -337,7 +353,7 @@ module OpenFoodNetwork sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, user, []) + applicator = OrderCycleFormApplicator.new(oc, user) incoming = true exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, incoming: incoming) variant1 = FactoryGirl.create(:variant) From 74b7feda530fccb95c10b5d5b7c9e69965a08320 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Mar 2015 15:49:48 +1100 Subject: [PATCH 281/384] coordinator of a simple order cycle has permission to add their own variants to outgoing exchanges --- lib/open_food_network/permissions.rb | 8 +++++++- spec/lib/open_food_network/permissions_spec.rb | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 391f016983..3063394171 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -139,6 +139,12 @@ module OpenFoodNetwork def visible_variants_for_outgoing_exchanges_between(coordinator, hub, options={}) return Spree::Variant.where("1=0") unless options[:order_cycle] if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(coordinator.id) + # Any variants produced by the coordinator, for outgoing exchanges with itself + coordinator_variants = [] + if hub == coordinator + coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', coordinator) + end + # Any variants of any producers that have granted the hub P-OC producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) @@ -150,7 +156,7 @@ module OpenFoodNetwork active_variants = exchange.variants end - Spree::Variant.where(id: permitted_variants | active_variants) + Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) else # Any variants produced by MY PRODUCERS, where my producer has granted P-OC to the hub producers = granting(:add_to_order_cycle, to: [hub], scope: managed_enterprises.is_primary_producer) diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 7874323924..420030a32d 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -376,6 +376,22 @@ module OpenFoodNetwork expect(visible).to_not include v2 end + context "where the coordinator produces products" do + let!(:v3) { create(:variant, product: create(:simple_product, supplier: e1)) } + + it "returns any variants produced by the coordinator itself for exchanges with 'self'" do + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e1, order_cycle: oc) + expect(visible).to include v3 + expect(visible).to_not include v1, v2 + end + + it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2, v3 + end + end + # TODO: for backwards compatability, remove later context "when an exchange exists between the coordinator and the hub within this order cycle" do let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } From 2d5118290b857f823a0bae1385ec8113e24a93d1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Mar 2015 15:50:23 +1100 Subject: [PATCH 282/384] Fixing broken order cycle feature specs --- spec/features/admin/order_cycles_spec.rb | 317 ++++++++++++++--------- 1 file changed, 198 insertions(+), 119 deletions(-) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 4bfd0b683c..95454934dc 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -67,6 +67,11 @@ feature %q{ v2 = create(:variant, product: product) distributor = create(:distributor_enterprise, name: 'My distributor', with_payment_and_shipping: true) + # Relationships required for interface to work + create(:enterprise_relationship, parent: supplier, child: coordinator, permissions_list: [:add_to_order_cycle]) + create(:enterprise_relationship, parent: distributor, child: coordinator, permissions_list: [:add_to_order_cycle]) + create(:enterprise_relationship, parent: supplier, child: distributor, permissions_list: [:add_to_order_cycle]) + # And some enterprise fees supplier_fee = create(:enterprise_fee, enterprise: supplier, name: 'Supplier fee') coordinator_fee = create(:enterprise_fee, enterprise: coordinator, name: 'Coord fee') @@ -249,13 +254,18 @@ feature %q{ initial_variants = oc.variants.sort_by &:id # And a coordinating, supplying and distributing enterprise with some products with variants - coordinator = create(:distributor_enterprise, name: 'My coordinator') + coordinator = oc.coordinator supplier = create(:supplier_enterprise, name: 'My supplier') distributor = create(:distributor_enterprise, name: 'My distributor', with_payment_and_shipping: true) product = create(:product, supplier: supplier) v1 = create(:variant, product: product) v2 = create(:variant, product: product) + # Relationships required for interface to work + create(:enterprise_relationship, parent: supplier, child: coordinator, permissions_list: [:add_to_order_cycle]) + create(:enterprise_relationship, parent: distributor, child: coordinator, permissions_list: [:add_to_order_cycle]) + create(:enterprise_relationship, parent: supplier, child: distributor, permissions_list: [:add_to_order_cycle]) + # And some enterprise fees supplier_fee1 = create(:enterprise_fee, enterprise: supplier, name: 'Supplier fee 1') supplier_fee2 = create(:enterprise_fee, enterprise: supplier, name: 'Supplier fee 2') @@ -274,7 +284,9 @@ feature %q{ fill_in 'order_cycle_name', with: 'Plums & Avos' fill_in 'order_cycle_orders_open_at', with: '2040-11-06 06:00:00' fill_in 'order_cycle_orders_close_at', with: '2040-11-13 17:00:00' - select 'My coordinator', from: 'order_cycle_coordinator_id' + + # CAN'T CHANGE COORDINATOR ANYMORE + # select 'My coordinator', from: 'order_cycle_coordinator_id' # And I configure some coordinator fees click_button 'Add coordinator fee' @@ -338,7 +350,7 @@ feature %q{ page.should have_selector "input[value='2040-11-06 06:00:00 +1100']" page.should have_selector "input[value='2040-11-13 17:00:00 +1100']" - page.should have_content 'My coordinator' + page.should have_content coordinator.name page.should have_selector 'td.suppliers', text: 'My supplier' page.should have_selector 'td.distributors', text: 'My distributor' @@ -467,7 +479,6 @@ feature %q{ end context "as an enterprise user" do - let!(:supplier_managed) { create(:supplier_enterprise, name: 'Managed supplier') } let!(:supplier_unmanaged) { create(:supplier_enterprise, name: 'Unmanaged supplier') } let!(:supplier_permitted) { create(:supplier_enterprise, name: 'Permitted supplier') } @@ -477,147 +488,215 @@ feature %q{ let!(:distributor_managed_fee) { create(:enterprise_fee, enterprise: distributor_managed, name: 'Managed distributor fee') } let!(:shipping_method) { create(:shipping_method, distributors: [distributor_managed, distributor_unmanaged, distributor_permitted]) } let!(:payment_method) { create(:payment_method, distributors: [distributor_managed, distributor_unmanaged, distributor_permitted]) } - - let!(:supplier_permitted_relationship) do - create(:enterprise_relationship, parent: supplier_permitted, child: supplier_managed, - permissions_list: [:add_to_order_cycle]) - end - let!(:distributor_permitted_relationship) do - create(:enterprise_relationship, parent: distributor_permitted, child: distributor_managed, - permissions_list: [:add_to_order_cycle]) - end let!(:product_managed) { create(:product, supplier: supplier_managed) } let!(:product_permitted) { create(:product, supplier: supplier_permitted) } before do - @new_user = create_enterprise_user - @new_user.enterprise_roles.build(enterprise: supplier_managed).save - @new_user.enterprise_roles.build(enterprise: distributor_managed).save + # Relationships required for interface to work + # Both suppliers allow both managed distributor to distribute their products (and add them to the order cycle) + create(:enterprise_relationship, parent: supplier_managed, child: distributor_managed, permissions_list: [:add_to_order_cycle]) + create(:enterprise_relationship, parent: supplier_permitted, child: distributor_managed, permissions_list: [:add_to_order_cycle]) - login_to_admin_as @new_user + # Both suppliers allow permitted distributor to distribute their products + create(:enterprise_relationship, parent: supplier_managed, child: distributor_permitted, permissions_list: [:add_to_order_cycle]) + create(:enterprise_relationship, parent: supplier_permitted, child: distributor_permitted, permissions_list: [:add_to_order_cycle]) + + # Permitted distributor can be added to the order cycle + create(:enterprise_relationship, parent: distributor_permitted, child: distributor_managed, permissions_list: [:add_to_order_cycle]) end - scenario "viewing a list of order cycles I am coordinating" do - oc_user_coordinating = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_unmanaged], coordinator: supplier_managed, distributors: [distributor_managed, distributor_unmanaged], name: 'Order Cycle 1' } ) - oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier_unmanaged, name: 'Order Cycle 2' } ) + context "that is a manager of the coordinator" do + before do + @new_user = create_enterprise_user + @new_user.enterprise_roles.build(enterprise: supplier_managed).save + @new_user.enterprise_roles.build(enterprise: distributor_managed).save - 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 show enterprises that I manage - page.should have_selector 'td.suppliers', text: supplier_managed.name - page.should have_selector 'td.distributors', text: distributor_managed.name - - # The order cycle should not show enterprises that I don't manage - page.should_not have_selector 'td.suppliers', text: supplier_unmanaged.name - page.should_not have_selector 'td.distributors', text: distributor_unmanaged.name - end - - scenario "creating a new order cycle" do - click_link "Order Cycles" - click_link 'New Order Cycle' - - save_screenshot '/Users/rob/Desktop/ss.png' - - # Select a coordinator since there are two available - select2_select 'Managed distributor', from: 'coordinator_id' - click_button "Continue >" - - save_screenshot '/Users/rob/Desktop/ss1.png' - - fill_in 'order_cycle_name', with: 'My order cycle' - fill_in 'order_cycle_orders_open_at', with: '2040-11-06 06:00:00' - fill_in 'order_cycle_orders_close_at', with: '2040-11-13 17:00:00' - - select 'Managed supplier', from: 'new_supplier_id' - click_button 'Add supplier' - select 'Permitted supplier', from: 'new_supplier_id' - click_button 'Add supplier' - - select_incoming_variant supplier_managed, 0, product_managed.master - select_incoming_variant supplier_permitted, 1, product_permitted.master - - select 'Managed distributor', from: 'order_cycle_coordinator_id' - click_button 'Add coordinator fee' - select 'Managed distributor fee', from: 'order_cycle_coordinator_fee_0_id' - - select 'Managed distributor', from: 'new_distributor_id' - click_button 'Add distributor' - select 'Permitted distributor', from: 'new_distributor_id' - click_button 'Add distributor' - - # Should only have suppliers / distributors listed which the user is managing or - # has E2E permission to add products to order cycles - page.should_not have_select 'new_supplier_id', with_options: [supplier_unmanaged.name] - page.should_not have_select 'new_distributor_id', with_options: [distributor_unmanaged.name] - - [distributor_unmanaged.name, supplier_managed.name, supplier_unmanaged.name].each do |enterprise_name| - page.should_not have_select 'order_cycle_coordinator_id', with_options: [enterprise_name] + login_to_admin_as @new_user end - click_button 'Create' + scenario "viewing a list of order cycles I am coordinating" do + oc_user_coordinating = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_unmanaged], name: 'Order Cycle 1' } ) + oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier_unmanaged, name: 'Order Cycle 2' } ) - flash_message.should == "Your order cycle has been created." - order_cycle = OrderCycle.find_by_name('My order cycle') - order_cycle.suppliers.sort.should == [supplier_managed, supplier_permitted].sort - order_cycle.coordinator.should == distributor_managed - order_cycle.distributors.sort.should == [distributor_managed, distributor_permitted].sort + 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 show enterprises that I manage + page.should have_selector 'td.suppliers', text: supplier_managed.name + page.should have_selector 'td.distributors', text: distributor_managed.name + + # The order cycle should not show enterprises that I don't manage + page.should_not have_selector 'td.suppliers', text: supplier_unmanaged.name + page.should_not have_selector 'td.distributors', text: distributor_unmanaged.name + end + + scenario "creating a new order cycle" do + click_link "Order Cycles" + click_link 'New Order Cycle' + + # We go straight through to the new form, because only one coordinator is available + + fill_in 'order_cycle_name', with: 'My order cycle' + fill_in 'order_cycle_orders_open_at', with: '2040-11-06 06:00:00' + fill_in 'order_cycle_orders_close_at', with: '2040-11-13 17:00:00' + + select 'Managed supplier', from: 'new_supplier_id' + click_button 'Add supplier' + select 'Permitted supplier', from: 'new_supplier_id' + click_button 'Add supplier' + + select_incoming_variant supplier_managed, 0, product_managed.master + select_incoming_variant supplier_permitted, 1, product_permitted.master + + select 'Managed distributor', from: 'order_cycle_coordinator_id' + click_button 'Add coordinator fee' + select 'Managed distributor fee', from: 'order_cycle_coordinator_fee_0_id' + + select 'Managed distributor', from: 'new_distributor_id' + click_button 'Add distributor' + select 'Permitted distributor', from: 'new_distributor_id' + click_button 'Add distributor' + + # Should only have suppliers / distributors listed which the user is managing or + # has E2E permission to add products to order cycles + page.should_not have_select 'new_supplier_id', with_options: [supplier_unmanaged.name] + page.should_not have_select 'new_distributor_id', with_options: [distributor_unmanaged.name] + + [distributor_unmanaged.name, supplier_managed.name, supplier_unmanaged.name].each do |enterprise_name| + page.should_not have_select 'order_cycle_coordinator_id', with_options: [enterprise_name] + end + + click_button 'Create' + + flash_message.should == "Your order cycle has been created." + order_cycle = OrderCycle.find_by_name('My order cycle') + order_cycle.suppliers.sort.should == [supplier_managed, supplier_permitted].sort + order_cycle.coordinator.should == distributor_managed + order_cycle.distributors.sort.should == [distributor_managed, distributor_permitted].sort + end + + scenario "editing an order cycle we can see (and for now, edit) all exchanges in the order cycle" do + # TODO: when we add the editable scope to variant permissions, we should test that + # exchanges with enterprises who have not granted P-OC to the coordinator are not + # editable, but at this point we cannot distiguish between visible and editable + # variants. + + oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) + + visit edit_admin_order_cycle_path(oc) + + # I should not see exchanges for supplier_unmanaged or distributor_unmanaged + page.all('tr.supplier').count.should == 3 + page.all('tr.distributor').count.should == 3 + + # When I save, then those exchanges should remain + click_button 'Update' + page.should have_content "Your order cycle has been updated." + + oc.reload + oc.suppliers.sort.should == [supplier_managed, supplier_permitted, supplier_unmanaged].sort + oc.coordinator.should == distributor_managed + oc.distributors.sort.should == [distributor_managed, distributor_permitted, distributor_unmanaged].sort + end + + scenario "editing an order cycle" do + oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) + + visit edit_admin_order_cycle_path(oc) + + # When I remove all the exchanges and save + page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").click + page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").click + page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").click + page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").click + click_button 'Update' + + # Then the exchanges should be removed + page.should have_content "Your order cycle has been updated." + + oc.reload + oc.suppliers.should == [supplier_unmanaged] + oc.coordinator.should == distributor_managed + oc.distributors.should == [distributor_unmanaged] + end + + scenario "cloning an order cycle" do + oc = create(:simple_order_cycle, coordinator: distributor_managed) + + click_link "Order Cycles" + first('a.clone-order-cycle').click + flash_message.should == "Your order cycle #{oc.name} has been cloned." + + # Then I should have clone of the order cycle + occ = OrderCycle.last + occ.name.should == "COPY OF #{oc.name}" + end end - scenario "editing an order cycle does not affect exchanges we don't manage" do - oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: supplier_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) + context "that is a manager of a participating producer" do + before do + @new_user = create_enterprise_user + @new_user.enterprise_roles.build(enterprise: supplier_managed).save - visit edit_admin_order_cycle_path(oc) + login_to_admin_as @new_user + end - # I should not see exchanges for supplier_unmanaged or distributor_unmanaged - page.all('tr.supplier').count.should == 2 - page.all('tr.distributor').count.should == 2 + scenario "editing an order cycle we can see only exchanges involving my producer, and any distributors of my products" do + oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) + v1 = create(:variant, product: create(:product, supplier: supplier_managed) ) + ex = oc.exchanges.where(sender_id: distributor_managed, receiver_id: distributor_unmanaged, incoming: false).first + ex.update_attributes(variant_ids: [v1.id]) - # When I save, then those exchanges should remain - click_button 'Update' - page.should have_content "Your order cycle has been updated." + visit edit_admin_order_cycle_path(oc) - oc.reload - oc.suppliers.sort.should == [supplier_managed, supplier_permitted, supplier_unmanaged].sort - oc.coordinator.should == supplier_managed - oc.distributors.sort.should == [distributor_managed, distributor_permitted, distributor_unmanaged].sort + # I should only see exchanges for supplier_managed AND + # distributor_managed and distributor_permitted (who I have given permission to) AND + # and distributor_unmanaged (who distributes my products) + page.all('tr.supplier').count.should == 1 + page.all('tr.distributor').count.should == 3 + + # When I save, any exchanges that I can't manage remain + click_button 'Update' + page.should have_content "Your order cycle has been updated." + + oc.reload + oc.suppliers.sort.should == [supplier_managed, supplier_permitted, supplier_unmanaged].sort + oc.coordinator.should == distributor_managed + oc.distributors.sort.should == [distributor_managed, distributor_permitted, distributor_unmanaged].sort + end end - scenario "editing an order cycle" do - oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: supplier_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) + context "that is the manager of a participating distributor" do + let(:my_distributor) { create(:distributor_enterprise) } - visit edit_admin_order_cycle_path(oc) + before do + @new_user = create_enterprise_user + @new_user.enterprise_roles.build(enterprise: my_distributor).save - # When I remove all the exchanges and save - page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").click - page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").click - page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").click - page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").click - click_button 'Update' + login_to_admin_as @new_user + end - # Then the exchanges should be removed - page.should have_content "Your order cycle has been updated." + scenario "editing an order cycle we can see only exchanges involving my producer, and any distributors of my products" do + oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [my_distributor, distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) - oc.reload - oc.suppliers.should == [supplier_unmanaged] - oc.coordinator.should == supplier_managed - oc.distributors.should == [distributor_unmanaged] - end + visit edit_admin_order_cycle_path(oc) + # I should only see exchanges for my_distributor + page.all('tr.supplier').count.should == 0 + page.all('tr.distributor').count.should == 1 - scenario "cloning an order cycle" do - oc = create(:simple_order_cycle, coordinator: distributor_managed) + # When I save, any exchange that I can't manage remain + click_button 'Update' + page.should have_content "Your order cycle has been updated." - click_link "Order Cycles" - first('a.clone-order-cycle').click - flash_message.should == "Your order cycle #{oc.name} has been cloned." - - # Then I should have clone of the order cycle - occ = OrderCycle.last - occ.name.should == "COPY OF #{oc.name}" + oc.reload + oc.suppliers.sort.should == [supplier_managed, supplier_permitted, supplier_unmanaged].sort + oc.coordinator.should == distributor_managed + oc.distributors.sort.should == [my_distributor, distributor_managed, distributor_permitted, distributor_unmanaged].sort + end end end From 6e77a5eebcf08eb7d7d1b6fe95083ef1fcf404a1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Mar 2015 16:47:12 +1100 Subject: [PATCH 283/384] Using the correct method name duh --- app/views/admin/order_cycles/_row.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 58986b280e..b1d01c74bf 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -8,7 +8,7 @@ - unless order_cycles_simple_index %td.suppliers - - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).enterprises_for(order_cycle: order_cycle))) + - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle: order_cycle)) - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 %span.with-tip{'data-powertip' => supplier_list} @@ -18,7 +18,7 @@ = supplier_list %td= order_cycle.coordinator.name %td.distributors - - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).enterprises_for(order_cycle: order_cycle))) + - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle: order_cycle)) - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 %span.with-tip{'data-powertip' => distributor_list} From d49dd621248940d4dabac9b0965ef76700ad3946 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 1 Apr 2015 11:38:06 +1100 Subject: [PATCH 284/384] Replace unused bugsnag notification with one to test for line items with a quantity of zero --- app/models/spree/order_decorator.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index c29d3f8165..9d213bd633 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -103,13 +103,17 @@ Spree::Order.class_eval do def add_variant(variant, quantity = 1, max_quantity = nil, currency = nil) line_items(:reload) current_item = find_line_item_by_variant(variant) - if current_item - Bugsnag.notify(RuntimeError.new("Order populator weirdness"), { + + # Notify bugsnag if we get line items with a quantity of zero + if quantity == 0 + Bugsnag.notify(RuntimeError.new("Zero Quantity Line Item"), { current_item: current_item.as_json, line_items: line_items.map(&:id), - reloaded: line_items(:reload).map(&:id), variant: variant.as_json }) + end + + if current_item current_item.quantity = quantity current_item.max_quantity = max_quantity From 54af6886c2ac1024cf8acc4661a54bfd7a2d2314 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 1 Apr 2015 12:34:22 +1100 Subject: [PATCH 285/384] Line items with a quantity of zero return price_with_adjustments of 0.0 --- app/models/spree/line_item_decorator.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index 28be16d1a4..b2c0a583fc 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -26,6 +26,7 @@ Spree::LineItem.class_eval do def price_with_adjustments # EnterpriseFee#create_locked_adjustment applies adjustments on line items to their parent order, # so line_item.adjustments returns an empty array + return 0 if quantity == 0 (price + order.adjustments.where(source_id: id).sum(&:amount) / quantity).round(2) end From aa7ddbcba454a97a4a24ee3ffc9e9bec081242e0 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 1 Apr 2015 14:38:15 +1100 Subject: [PATCH 286/384] Renaming Dist. column to Admin & Handling --- app/controllers/spree/admin/reports_controller_decorator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 0f818d319b..2c0b0180ea 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -102,7 +102,7 @@ Spree::Admin::ReportsController.class_eval do send_data csv_string, :filename => "orders_and_distributors_#{timestamp}.csv" end end - + def sales_tax params[:q] ||= {} @@ -525,7 +525,7 @@ Spree::Admin::ReportsController.class_eval do table_items = @line_items @include_blank = 'All' - header = ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant", "Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Dist (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?", + header = ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant", "Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?", "Shipping", "Delivery?", "Ship street", "Ship street 2", "Ship city", "Ship postcode", "Ship state", "Order notes"] rsa = proc { |line_items| line_items.first.order.shipping_method.andand.require_ship_address } From 588e036c1d37e6663c03a5d218e54236f8ce49da Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 1 Apr 2015 14:45:23 +1100 Subject: [PATCH 287/384] Adding SKU to customer totals report --- app/controllers/spree/admin/reports_controller_decorator.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 2c0b0180ea..b1f229038d 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -526,7 +526,7 @@ Spree::Admin::ReportsController.class_eval do @include_blank = 'All' header = ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant", "Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?", - "Shipping", "Delivery?", "Ship street", "Ship street 2", "Ship city", "Ship postcode", "Ship state", "Order notes"] + "Shipping", "Delivery?", "Ship street", "Ship street 2", "Ship city", "Ship postcode", "Ship state", "Order notes", "SKU"] rsa = proc { |line_items| line_items.first.order.shipping_method.andand.require_ship_address } @@ -553,7 +553,8 @@ Spree::Admin::ReportsController.class_eval do proc { |line_items| line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items) }, proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) }, - proc { |line_items| line_items.first.order.special_instructions }] + proc { |line_items| line_items.first.order.special_instructions } , + proc { |line_items| line_items.first.variant.product.sku } ] rules = [ { group_by: proc { |line_item| line_item.order.distributor }, sort_by: proc { |distributor| distributor.name } }, @@ -582,6 +583,7 @@ Spree::Admin::ReportsController.class_eval do proc { |line_items| "" }, proc { |line_items| "" }, + proc { |line_items| "" }, proc { |line_items| "" } ] }, { group_by: proc { |line_item| line_item.variant.product }, From 94a88278a9e44962436c8c9a573322ae10db9b5d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 1 Apr 2015 15:46:11 +1100 Subject: [PATCH 288/384] Ignore the unit_to_display when it is wholly contained within display_name or vice versa --- app/models/spree/variant_decorator.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 13908382f9..64fbe7110b 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -76,6 +76,8 @@ Spree::Variant.class_eval do def full_name return unit_to_display if display_name.blank? + return display_name if display_name.scan(/#{unit_to_display}/i).any? + return unit_to_display if unit_to_display.scan(/#{display_name}/i).any? display_name + " (" + unit_to_display + ")" end From 8d95ba2c6932ac2d41197060b7226c9b1f4724a7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 1 Apr 2015 15:46:19 +1100 Subject: [PATCH 289/384] Display full name of variant on order cycle edit form --- app/views/admin/enterprises/_supplied_product.rabl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/enterprises/_supplied_product.rabl b/app/views/admin/enterprises/_supplied_product.rabl index 268ff302d5..14835f2dbe 100644 --- a/app/views/admin/enterprises/_supplied_product.rabl +++ b/app/views/admin/enterprises/_supplied_product.rabl @@ -6,5 +6,5 @@ node(:image_url) { |p| p.images.present? ? p.images.first.attachment.url(:mini) node(:master_id) { |p| p.master.id } child variants: :variants do |variant| attributes :id - node(:label) { |v| v.options_text } + node(:label) { |v| v.full_name } end From ca1a5b5f8d572a06f4679129434a3ba730a8b090 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 08:54:35 +1100 Subject: [PATCH 290/384] Ignoring any new producer properties submitted by the user --- .../admin/enterprises_controller.rb | 10 +++++ app/models/enterprise.rb | 1 + .../admin/enterprises_controller_spec.rb | 45 +++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 7e3ab9d17a..c54f1f976f 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -11,6 +11,7 @@ module Admin before_filter :check_can_change_owner, only: :update before_filter :check_can_change_bulk_owner, only: :bulk_update before_filter :check_can_change_managers, only: :update + before_filter :strip_new_properties, only: [:create, :update] helper 'spree/products' @@ -162,6 +163,15 @@ module Admin end end + def strip_new_properties + unless spree_current_user.admin? || params[:enterprise][:producer_properties_attributes].nil? + names = Spree::Property.pluck(:name) + params[:enterprise][:producer_properties_attributes].each do |key, property| + params[:enterprise][:producer_properties_attributes].delete key unless names.include? property[:property_name] + end + end + end + # Overriding method on Spree's resource controller def location_after_save if params[:enterprise].key? :producer_properties_attributes diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 462910aa0d..94ce2e7919 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -15,6 +15,7 @@ class Enterprise < ActiveRecord::Base has_and_belongs_to_many :groups, class_name: 'EnterpriseGroup' has_many :producer_properties, foreign_key: 'producer_id' + has_many :properties, through: :producer_properties has_many :supplied_products, :class_name => 'Spree::Product', :foreign_key => 'supplier_id', :dependent => :destroy has_many :distributed_orders, :class_name => 'Spree::Order', :foreign_key => 'distributor_id' belongs_to :address, :class_name => 'Spree::Address' diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index c43154f3fd..6cbe2c6f95 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -135,6 +135,51 @@ module Admin distributor.reload expect(distributor.users).to_not include user end + + + describe "enterprise properties" do + let(:producer) { create(:enterprise) } + let!(:property) { create(:property, name: "A nice name") } + + before do + @request.env['HTTP_REFERER'] = 'http://test.com/' + login_as_enterprise_user [producer] + end + + context "when a submitted property does not already exist" do + it "does not create a new property, or product property" do + spree_put :update, { + id: producer, + enterprise: { + producer_properties_attributes: { + '0' => { property_name: 'a different name', value: 'something' } + } + } + } + expect(Spree::Property.count).to be 1 + expect(ProducerProperty.count).to be 0 + property_names = producer.reload.properties.map(&:name) + expect(property_names).to_not include 'a different name' + end + end + + context "when a submitted property exists" do + it "adds a product property" do + spree_put :update, { + id: producer, + enterprise: { + producer_properties_attributes: { + '0' => { property_name: 'A nice name', value: 'something' } + } + } + } + expect(Spree::Property.count).to be 1 + expect(ProducerProperty.count).to be 1 + property_names = producer.reload.properties.map(&:name) + expect(property_names).to include 'A nice name' + end + end + end end context "as owner" do From 8ed6653dc4d5738681541885edc57be31bfce6a1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 08:55:20 +1100 Subject: [PATCH 291/384] Ignoring any new product properties submitted by a non-admin user --- .../admin/products_controller_decorator.rb | 10 ++++ .../spree/admin/products_controller_spec.rb | 49 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/app/controllers/spree/admin/products_controller_decorator.rb b/app/controllers/spree/admin/products_controller_decorator.rb index c0e1f8b948..1591586f76 100644 --- a/app/controllers/spree/admin/products_controller_decorator.rb +++ b/app/controllers/spree/admin/products_controller_decorator.rb @@ -5,6 +5,7 @@ Spree::Admin::ProductsController.class_eval do include OrderCyclesHelper before_filter :load_form_data, :only => [:bulk_edit, :new, :create, :edit, :update] before_filter :load_spree_api_key, :only => [:bulk_edit, :variant_overrides] + before_filter :strip_new_properties, only: [:create, :update] alias_method :location_after_save_original, :location_after_save @@ -95,4 +96,13 @@ Spree::Admin::ProductsController.class_eval do @producers = OpenFoodNetwork::Permissions.new(spree_current_user).managed_product_enterprises.is_primary_producer.by_name @taxons = Spree::Taxon.order(:name) end + + def strip_new_properties + unless spree_current_user.admin? || params[:product][:product_properties_attributes].nil? + names = Spree::Property.pluck(:name) + params[:product][:product_properties_attributes].each do |key, property| + params[:product][:product_properties_attributes].delete key unless names.include? property[:property_name] + end + end + end end diff --git a/spec/controllers/spree/admin/products_controller_spec.rb b/spec/controllers/spree/admin/products_controller_spec.rb index 9746ffa0e2..b8d76abd9c 100644 --- a/spec/controllers/spree/admin/products_controller_spec.rb +++ b/spec/controllers/spree/admin/products_controller_spec.rb @@ -63,4 +63,53 @@ describe Spree::Admin::ProductsController do response.should redirect_to "/admin/products/new" end end + + describe "updating" do + describe "product properties" do + context "as an enterprise user" do + let(:producer) { create(:enterprise) } + let!(:product) { create(:simple_product, supplier: producer) } + let!(:property) { create(:property, name: "A nice name") } + + before do + @request.env['HTTP_REFERER'] = 'http://test.com/' + login_as_enterprise_user [producer] + end + + context "when a submitted property does not already exist" do + it "does not create a new property, or product property" do + spree_put :update, { + id: product, + product: { + product_properties_attributes: { + '0' => { property_name: 'a different name', value: 'something' } + } + } + } + expect(Spree::Property.count).to be 1 + expect(Spree::ProductProperty.count).to be 0 + property_names = product.reload.properties.map(&:name) + expect(property_names).to_not include 'a different name' + end + end + + context "when a submitted property exists" do + it "adds a product property" do + spree_put :update, { + id: product, + product: { + product_properties_attributes: { + '0' => { property_name: 'A nice name', value: 'something' } + } + } + } + expect(Spree::Property.count).to be 1 + expect(Spree::ProductProperty.count).to be 1 + property_names = product.reload.properties.map(&:name) + expect(property_names).to include 'A nice name' + end + end + end + end + end end From 5b235f356e0ee07be45ecb66f9c8f30c8f8aeec6 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 08:56:36 +1100 Subject: [PATCH 292/384] Replace free text with with select dropdown for property names when not super admin --- .../producer_properties/_producer_property_fields.html.haml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/admin/producer_properties/_producer_property_fields.html.haml b/app/views/admin/producer_properties/_producer_property_fields.html.haml index 79dc815f9d..314cbfc31a 100644 --- a/app/views/admin/producer_properties/_producer_property_fields.html.haml +++ b/app/views/admin/producer_properties/_producer_property_fields.html.haml @@ -4,7 +4,10 @@ %span.handle = f.hidden_field :id %td.property_name - = f.text_field :property_name, :class => 'autocomplete' + - if spree_current_user.admin? + = f.text_field :property_name, :class => 'autocomplete' + - else + = f.select :property_name, @properties, { :include_blank => true }, { class: 'select2 fullwidth' } %td.value = f.text_field :value, :class => 'autocomplete' %td.actions From 8926e3765e8436aefe850dbeaa15ed6029125c0c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 08:57:15 +1100 Subject: [PATCH 293/384] Replace free text with with select dropdown for product property names when not super admin --- .../replace_free_text_with_select.html.haml.deface | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 app/overrides/spree/admin/product_properties/_product_property_fields/replace_free_text_with_select.html.haml.deface diff --git a/app/overrides/spree/admin/product_properties/_product_property_fields/replace_free_text_with_select.html.haml.deface b/app/overrides/spree/admin/product_properties/_product_property_fields/replace_free_text_with_select.html.haml.deface new file mode 100644 index 0000000000..486d35014e --- /dev/null +++ b/app/overrides/spree/admin/product_properties/_product_property_fields/replace_free_text_with_select.html.haml.deface @@ -0,0 +1,6 @@ +/ replace_contents "td.property_name" + +- if spree_current_user.admin? + = f.text_field :property_name, :class => 'autocomplete' +- else + = f.select :property_name, @properties, { :include_blank => true }, { class: 'select2 fullwidth' } From 3aa5f6e0235ddc3bcfc19f87f252f499ce38e6d4 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 11:30:32 +1100 Subject: [PATCH 294/384] Adding properties tab to admin enterprise edit form --- .../controllers/side_menu_controller.js.coffee | 4 ++++ .../admin/enterprises_controller.rb | 10 ++++++++++ app/views/admin/enterprises/_form.html.haml | 4 ++++ .../enterprises/form/_properties.html.haml | 12 ++++++++++++ .../admin/producer_properties/_form.html.haml | 12 ++++++++++++ .../admin/producer_properties/index.html.haml | 18 ++++-------------- 6 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 app/views/admin/enterprises/form/_properties.html.haml create mode 100644 app/views/admin/producer_properties/_form.html.haml diff --git a/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee index a74c3005e0..d5d0e1681a 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee @@ -13,6 +13,7 @@ angular.module("admin.enterprises") { name: 'About', icon_class: "icon-pencil" } { name: 'Business Details', icon_class: "icon-briefcase" } { name: 'Images', icon_class: "icon-picture" } + { name: "Properties", icon_class: "icon-tags", show: "showProperties()" } { name: "Shipping Methods", icon_class: "icon-truck", show: "showShippingMethods()" } { name: "Payment Methods", icon_class: "icon-money", show: "showPaymentMethods()" } { name: "Enterprise Fees", icon_class: "icon-tasks", show: "showEnterpriseFees()" } @@ -28,6 +29,9 @@ angular.module("admin.enterprises") else true + $scope.showProperties = -> + !!$scope.Enterprise.is_primary_producer + $scope.showShippingMethods = -> enterprisePermissions.can_manage_shipping_methods && $scope.Enterprise.sells != "none" diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index c54f1f976f..e745d22909 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -12,6 +12,8 @@ module Admin before_filter :check_can_change_bulk_owner, only: :bulk_update before_filter :check_can_change_managers, only: :update before_filter :strip_new_properties, only: [:create, :update] + before_filter :load_properties, only: [:edit, :update] + before_filter :setup_property, only: [:edit] helper 'spree/products' @@ -172,6 +174,14 @@ module Admin end end + def load_properties + @properties = Spree::Property.pluck(:name) + end + + def setup_property + @enterprise.producer_properties.build + end + # Overriding method on Spree's resource controller def location_after_save if params[:enterprise].key? :producer_properties_attributes diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index aa2ae7f589..28f76d34d0 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -31,6 +31,10 @@ %legend Images = render 'admin/enterprises/form/images', f: f +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Properties'" } } + %legend Properties + = render 'admin/enterprises/form/properties', f: f + %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Shipping Methods'" } } %legend Shipping Methods = render 'admin/enterprises/form/shipping_methods', f: f diff --git a/app/views/admin/enterprises/form/_properties.html.haml b/app/views/admin/enterprises/form/_properties.html.haml new file mode 100644 index 0000000000..795a104f1a --- /dev/null +++ b/app/views/admin/enterprises/form/_properties.html.haml @@ -0,0 +1,12 @@ += render 'admin/producer_properties/form', f: f + +// :javascript +// var properties = #{raw(@properties.to_json)}; +// +// $("#producer_properties input.autocomplete").live("keydown", function() { +// already_auto_completed = $(this).is('ac_input'); +// if (!already_auto_completed) { +// $(this).autocomplete({source: properties}); +// $(this).focus(); +// } +// }); diff --git a/app/views/admin/producer_properties/_form.html.haml b/app/views/admin/producer_properties/_form.html.haml new file mode 100644 index 0000000000..dcd83f8ec4 --- /dev/null +++ b/app/views/admin/producer_properties/_form.html.haml @@ -0,0 +1,12 @@ +%fieldset.no-border-top + .add_producer_properties{"data-hook" => "add_producer_properties"} + = image_tag 'spinner.gif', :plugin => 'spree', :style => 'display:none;', :id => 'busy_indicator' + %table.index.sortable{"data-hook" => "", "data-sortable-link" => main_app.update_positions_admin_enterprise_producer_properties_url(@enterprise)} + %thead + %tr{"data-hook" => "producer_properties_header"} + %th{colspan: "2"} Property + %th Value + %th.actions + %tbody#producer_properties{"data-hook" => ""} + = f.fields_for :producer_properties do |pp_form| + = render 'admin/producer_properties/producer_property_fields', f: pp_form diff --git a/app/views/admin/producer_properties/index.html.haml b/app/views/admin/producer_properties/index.html.haml index 2b4277b08d..d8eac9e816 100644 --- a/app/views/admin/producer_properties/index.html.haml +++ b/app/views/admin/producer_properties/index.html.haml @@ -13,20 +13,10 @@ = form_for @enterprise, url: main_app.admin_enterprise_path(@enterprise), method: :put do |f| - %fieldset.no-border-top - .add_producer_properties{"data-hook" => "add_producer_properties"} - = image_tag 'spinner.gif', :plugin => 'spree', :style => 'display:none;', :id => 'busy_indicator' - %table.index.sortable{"data-hook" => "", "data-sortable-link" => main_app.update_positions_admin_enterprise_producer_properties_url(@enterprise)} - %thead - %tr{"data-hook" => "producer_properties_header"} - %th{colspan: "2"} Property - %th Value - %th.actions - %tbody#producer_properties{"data-hook" => ""} - = f.fields_for :producer_properties do |pp_form| - = render 'producer_property_fields', f: pp_form - = render 'spree/admin/shared/edit_resource_links', collection_url: main_app.admin_enterprise_producer_properties_path(@enterprise) - = hidden_field_tag 'clear_producer_properties', 'true' + = render 'form', f: f + + = render 'spree/admin/shared/edit_resource_links', collection_url: main_app.admin_enterprise_producer_properties_path(@enterprise) + = hidden_field_tag 'clear_producer_properties', 'true' :javascript var properties = #{raw(@properties.to_json)}; From cb376602f2b4145157fead366426bac38654f7bc Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 14:03:51 +1100 Subject: [PATCH 295/384] Override redirect to index page when editing properties from edit page --- app/controllers/admin/enterprises_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index e745d22909..aad2430570 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -184,7 +184,7 @@ module Admin # Overriding method on Spree's resource controller def location_after_save - if params[:enterprise].key? :producer_properties_attributes + if params[:enterprise].key? :producer_properties_attributes && request.referrer != main_app.edit_admin_enterprise_path(@enterprise) main_app.admin_enterprises_path else main_app.edit_admin_enterprise_path(@enterprise) From 5020eb4e32e4dd8ad126e58281262914e6099125 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 14:14:16 +1100 Subject: [PATCH 296/384] And again: override redirect to index page when editing properties from edit page --- app/controllers/admin/enterprises_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index aad2430570..b9ae54339b 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -184,7 +184,8 @@ module Admin # Overriding method on Spree's resource controller def location_after_save - if params[:enterprise].key? :producer_properties_attributes && request.referrer != main_app.edit_admin_enterprise_path(@enterprise) + refered_from_edit = URI(request.referer).path == main_app.edit_admin_enterprise_path(@enterprise) + if params[:enterprise].key?(:producer_properties_attributes) && !refered_from_edit main_app.admin_enterprises_path else main_app.edit_admin_enterprise_path(@enterprise) From 76648b97b01da1f025d1053e3ea94c548db2f181 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 14:15:00 +1100 Subject: [PATCH 297/384] Update spec, properties must exist for enterprise users before they can be assigned --- spec/features/admin/enterprises_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 0cd6f08451..a75125bdb5 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -474,11 +474,12 @@ feature %q{ end scenario "managing producer properties", js: true do + create(:property, name: "Certified Organic") click_link 'Enterprises' within(".enterprise-#{supplier1.id}") { click_link 'Properties' } - # -- Create / update - fill_in 'enterprise_producer_properties_attributes_0_property_name', with: "Certified Organic" + # -- Update only + select2_select "Certified Organic", from: 'enterprise_producer_properties_attributes_0_property_name' fill_in 'enterprise_producer_properties_attributes_0_value', with: "NASAA 12345" click_button 'Update' page.should have_selector '#listing_enterprises a', text: supplier1.name From 1e1d52cc4729fa6acb81de07cb20dc4c7e07de89 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 14:35:39 +1100 Subject: [PATCH 298/384] Adding 'selected' option to have_select2 matcher --- spec/support/matchers/select2_matchers.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/support/matchers/select2_matchers.rb b/spec/support/matchers/select2_matchers.rb index 8b7bc2f834..67cfcd81d0 100644 --- a/spec/support/matchers/select2_matchers.rb +++ b/spec/support/matchers/select2_matchers.rb @@ -14,6 +14,10 @@ RSpec::Matchers.define :have_select2 do |id, options={}| results << node.has_selector?(from) + if results.all? + results << selected_option_is(from, options[:selected]) if options.key? :selected + end + if results.all? results << all_options_present(from, options[:with_options]) if options.key? :with_options results << exact_options_present(from, options[:options]) if options.key? :options @@ -47,6 +51,12 @@ RSpec::Matchers.define :have_select2 do |id, options={}| end end + def selected_option_is(from, text) + within find(from) do + find("a.select2-choice").text == text + end + end + def with_select2_open(from) find(from).click r = yield From 5d7659aa3ce8629c14f014954a0a32e5e95fb8bc Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 14:36:19 +1100 Subject: [PATCH 299/384] Fixing product spec, using have_select2 instead of have_field --- spec/features/admin/products_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index ce6b2aba7a..740de12045 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -66,7 +66,7 @@ feature %q{ select2_select @enterprise_fees[2].name, :from => 'product_product_distributions_attributes_2_enterprise_fee_id' click_button 'Update' - + product.reload product.distributors.sort.should == [@distributors[0], @distributors[2]].sort @@ -186,16 +186,16 @@ feature %q{ # When I navigate to the product properties page visit spree.admin_product_product_properties_path(p) - page.should have_field 'product_product_properties_attributes_0_property_name', with: 'fooprop', visible: true - page.should have_field 'product_product_properties_attributes_0_value', with: 'fooval', visible: true + page.should have_select2 'product_product_properties_attributes_0_property_name', selected: 'fooprop' + page.should have_field 'product_product_properties_attributes_0_value', with: 'fooval' # And I delete the property page.all('a.remove_fields').first.click wait_until { p.reload.property('fooprop').nil? } # Then the property should have been deleted - page.should_not have_field 'product_product_properties_attributes_0_property_name', with: 'fooprop', visible: true - page.should_not have_field 'product_product_properties_attributes_0_value', with: 'fooval', visible: true + page.should_not have_field 'product_product_properties_attributes_0_property_name', with: 'fooprop' + page.should_not have_field 'product_product_properties_attributes_0_value', with: 'fooval' end @@ -205,13 +205,13 @@ feature %q{ Spree::Image.create({:viewable_id => product.master.id, :viewable_type => 'Spree::Variant', :alt => "position 1", :attachment => image, :position => 1}) visit spree.admin_product_images_path(product) - page.should have_selector "table[data-hook='images_table'] td img", visible: true + page.should have_selector "table[data-hook='images_table'] td img" product.reload.images.count.should == 1 page.find('a.delete-resource').click wait_until { product.reload.images.count == 0 } - page.should_not have_selector "table[data-hook='images_table'] td img", visible: true + page.should_not have_selector "table[data-hook='images_table'] td img" end end end From 3b9824171ab91d354b1a5e4a6d7cf8050fcc9d9c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 15:53:32 +1100 Subject: [PATCH 300/384] Adding with_order_cycles_as_supplier_outer scope to Enterprise --- app/models/enterprise.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 94ce2e7919..78a40b7c60 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -115,6 +115,9 @@ class Enterprise < ActiveRecord::Base scope :with_distributed_products_outer, joins('LEFT OUTER JOIN product_distributions ON product_distributions.distributor_id = enterprises.id'). joins('LEFT OUTER JOIN spree_products ON spree_products.id = product_distributions.product_id') + scope :with_order_cycles_as_supplier_outer, + joins("LEFT OUTER JOIN exchanges ON (exchanges.sender_id = enterprises.id AND exchanges.incoming = 't')"). + joins('LEFT OUTER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)') scope :with_order_cycles_as_distributor_outer, joins("LEFT OUTER JOIN exchanges ON (exchanges.receiver_id = enterprises.id AND exchanges.incoming = 'f')"). joins('LEFT OUTER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)') From a10de68430f8de0e33ed142ace974edbf610a361 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Apr 2015 15:55:03 +1100 Subject: [PATCH 301/384] only load permissions once in serializer --- app/serializers/api/admin/order_cycle_serializer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 333cdfacdc..68d67e4bb4 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -21,10 +21,10 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be visible within outgoing exchanges from that enterprise visible = {} - enterprises = OpenFoodNetwork::Permissions.new(options[:current_user]).order_cycle_enterprises_for(order_cycle: object) + permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: object) enterprises.each do |enterprise| - variants = OpenFoodNetwork::Permissions.new(options[:current_user]). - visible_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) + variants = permissions.visible_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) visible[enterprise.id] = variants if variants.any? end visible From d28fc7e42d2aeba64d2e8d366093a810d6999638 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 3 Apr 2015 14:58:01 +1100 Subject: [PATCH 302/384] Hubs can see incoming exchanges of producers who have given them P-OC or whose variants they are actively distributing --- .../api/admin/exchange_serializer.rb | 2 +- .../order_cycle_form_applicator.rb | 2 +- lib/open_food_network/permissions.rb | 64 +++++- .../lib/open_food_network/permissions_spec.rb | 212 +++++++++++++----- 4 files changed, 210 insertions(+), 70 deletions(-) diff --git a/app/serializers/api/admin/exchange_serializer.rb b/app/serializers/api/admin/exchange_serializer.rb index bd143534e7..4fa7760de5 100644 --- a/app/serializers/api/admin/exchange_serializer.rb +++ b/app/serializers/api/admin/exchange_serializer.rb @@ -7,7 +7,7 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer permitted = Spree::Variant.where("1=0") if object.incoming permitted = OpenFoodNetwork::Permissions.new(options[:current_user]). - visible_variants_for_incoming_exchanges_between(object.sender, object.receiver) + visible_variants_for_incoming_exchanges_between(object.sender, object.receiver, order_cycle: object.order_cycle) else permitted = OpenFoodNetwork::Permissions.new(options[:current_user]). visible_variants_for_outgoing_exchanges_between(object.sender, object.receiver, order_cycle: object.order_cycle) diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index b8ecd9fa84..8f36a367ac 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -108,7 +108,7 @@ module OpenFoodNetwork # TODO Need to use editable rather than visible def editable_variant_ids_for_incoming_exchange_between(sender, receiver) OpenFoodNetwork::Permissions.new(@spree_current_user). - visible_variants_for_incoming_exchanges_between(sender, receiver). + visible_variants_for_incoming_exchanges_between(sender, receiver, order_cycle: @order_cycle). pluck(:id) end diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 3063394171..86c94c31b7 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -57,6 +57,9 @@ module OpenFoodNetwork # Any hubs that have been granted P-OC by producers I manage hubs_permitted = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer, scope: Enterprise.is_hub).pluck(:id) + # Any producers that have granted P-OC to hubs I manage + producers_permitted = granting(:add_to_order_cycle, to: managed_enterprises.is_hub, scope: Enterprise.is_primary_producer).pluck(:id) + managed_active = [] hubs_active = [] if order_cycle @@ -71,7 +74,7 @@ module OpenFoodNetwork hubs_active = active_exchanges.map(&:receiver_id) end - Enterprise.where(id: coordinator_permitted | managed_permitted | managed_active | hubs_permitted | hubs_active) + Enterprise.where(id: coordinator_permitted | managed_permitted | managed_active | hubs_permitted | producers_permitted | hubs_active) end end @@ -118,18 +121,28 @@ module OpenFoodNetwork # Find the exchanges of an order cycle that an admin can manage def order_cycle_exchanges(order_cycle) - ids = order_cycle_exchange_ids_involving_my_enterprises(order_cycle) | order_cycle_exchange_ids_distributing_my_variants(order_cycle) + ids = order_cycle_exchange_ids_involving_my_enterprises(order_cycle) | + order_cycle_exchange_ids_distributing_my_variants(order_cycle) | + order_cycle_exchange_ids_with_distributable_variants(order_cycle) Exchange.where(id: ids, order_cycle_id: order_cycle) end # Find the variants that a user can POTENTIALLY see within incoming exchanges - def visible_variants_for_incoming_exchanges_between(producer, coordinator) + def visible_variants_for_incoming_exchanges_between(producer, coordinator, options={}) + return Spree::Variant.where("1=0") unless options[:order_cycle] if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) # All variants belonging to the producer Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) else - Spree::Variant.where("1=0") # None + # All variants of the producer if it has granted P-OC to any of my managed hubs that are in this order cycle + permitted = EnterpriseRelationship.permitting(managed_hubs_in(options[:order_cycle])). + permitted_by(producer).with_permission(:add_to_order_cycle).present? + if permitted + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + Spree::Variant.where("1=0") + end end end @@ -158,8 +171,8 @@ module OpenFoodNetwork Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) else - # Any variants produced by MY PRODUCERS, where my producer has granted P-OC to the hub - producers = granting(:add_to_order_cycle, to: [hub], scope: managed_enterprises.is_primary_producer) + # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub + producers = granting(:add_to_order_cycle, to: [hub], scope: managed_producers_in(options[:order_cycle])) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) # PLUS any of my producers variants that are already in an outgoing exchange of this hub, so things don't break @@ -201,6 +214,16 @@ module OpenFoodNetwork Enterprise.managed_by(@user) end + def managed_hubs_in(order_cycle) + Enterprise.with_order_cycles_as_distributor_outer.where("order_cycles.id = (?)", order_cycle.id) + .merge(managed_enterprises.is_hub) + end + + def managed_producers_in(order_cycle) + Enterprise.with_order_cycles_as_supplier_outer.where("order_cycles.id = (?)", order_cycle.id) + .merge(managed_enterprises.is_primary_producer) + end + def related_enterprises_with(permission) parent_ids = EnterpriseRelationship. permitting(managed_enterprises). @@ -241,16 +264,37 @@ module OpenFoodNetwork order_cycle.exchanges.involving(managed_enterprises).pluck :id end + def order_cycle_exchange_ids_with_distributable_variants(order_cycle) + # Find my managed hubs in this order cycle + hubs = managed_hubs_in(order_cycle) + # Any incoming exchange where the producer has granted P-OC to one or more of those hubs + producers = granting(:add_to_order_cycle, to: hubs, scope: Enterprise.is_primary_producer).pluck :id + permitted_exchanges = order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id + + # TODO: remove active_exchanges when we think it is safe to do so + # active_exchanges is for backward compatability, before we restricted variants in each + # outgoing exchange to those where the producer had granted P-OC to the distributor + # For any of my managed hubs in this OC, any incoming exchanges supplying variants in my outgoing exchanges + variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", hubs, order_cycle).pluck(:id).uniq + products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq + producers = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq + active_exchanges = order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id + + permitted_exchanges | active_exchanges + end + def order_cycle_exchange_ids_distributing_my_variants(order_cycle) - # Any outgoing exchange where the distributor has been granted P-OC by one or more of my producers - hubs = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer, scope: Enterprise.is_hub).pluck(:id) - permitted_exchanges = order_cycle.exchanges.outgoing.where(receiver_id: hubs) + # Find my producers in this order cycle + producers = managed_producers_in(order_cycle).pluck :id + # Any outgoing exchange where the distributor has been granted P-OC by one or more of those producers + hubs = granted(:add_to_order_cycle, by: producers, scope: Enterprise.is_hub) + permitted_exchanges = order_cycle.exchanges.outgoing.where(receiver_id: hubs).pluck :id # TODO: remove active_exchanges when we think it is safe to do so # active_exchanges is for backward compatability, before we restricted variants in each # outgoing exchange to those where the producer had granted P-OC to the distributor # For any of my managed producers, any outgoing exchanges with their variants - variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants).pluck :id permitted_exchanges | active_exchanges diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 420030a32d..f1e21837e8 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -45,7 +45,7 @@ module OpenFoodNetwork permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } end - context "where the coordinator sells own" do + context "where the coordinator sells any" do it "returns enterprises which have granted P-OC to the coordinator" do enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) expect(enterprises).to include hub @@ -63,7 +63,7 @@ module OpenFoodNetwork end end - context "as a manager of a hub that has granted P-OC to the coordinator" do + context "as a manager of a hub" do before do permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } end @@ -256,64 +256,133 @@ module OpenFoodNetwork end describe "finding exchanges of an order cycle that an admin can manage" do + let!(:producer) { create(:supplier_enterprise) } let(:oc) { create(:simple_order_cycle) } - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2) } - before do - permissions.stub(:managed_enterprises) { Enterprise.where('1=0') } - permissions.stub(:related_enterprises_with) { Enterprise.where('1=0') } - end + describe "as the manager of the coordinator" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - it "returns exchanges involving enterprises managed by the user" do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1, e2]) } - permissions.order_cycle_exchanges(oc).should == [ex] - end - - it "does not return exchanges involving enterprises with E2E permission" do - permissions.stub(:related_enterprises_with) { Enterprise.where(id: [e1, e2]) } - permissions.order_cycle_exchanges(oc).should == [] - end - - it "returns exchanges involving only the sender" do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - permissions.order_cycle_exchanges(oc).should == [ex] - end - - it "returns exchanges involving only the receiver" do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - permissions.order_cycle_exchanges(oc).should == [ex] - end - - describe "special permissions for managers of producers" do - let!(:producer) { create(:supplier_enterprise) } before do - ex.incoming = false - ex.save + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } end - it "returns outgoing exchanges where the hub has been granted P-OC by a supplier I manage" do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } - create(:enterprise_relationship, parent: producer, child: e2, permissions_list: [:add_to_order_cycle]) + it "returns all exchanges in the order cycle, regardless of E2E permissions" do + permissions.order_cycle_exchanges(oc).should include ex_in, ex_out + end + end - permissions.order_cycle_exchanges(oc).should == [ex] + + describe "as the manager of a hub" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } end + context "where my hub is in the order cycle" do + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - it "does not return outgoing exchanges where only the coordinator has been granted P-OC by a producer I manage" do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } - create(:enterprise_relationship, parent: producer, child: e1, permissions_list: [:add_to_order_cycle]) + it "returns my hub's outgoing exchange" do + permissions.order_cycle_exchanges(oc).should == [ex_out] + end - permissions.order_cycle_exchanges(oc).should == [] + context "where my hub has been granted P-OC by an incoming producer" do + before do + create(:enterprise_relationship, parent: producer, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns the producer's incoming exchange" do + permissions.order_cycle_exchanges(oc).should include ex_in + end + end + + context "where my hub has not been granted P-OC by an incoming producer" do + it "returns the producers's incoming exchange, and my own outhoing exchange" do + permissions.order_cycle_exchanges(oc).should_not include ex_in + end + end + end + + context "where my hub isn't in the order cycle" do + it "does not return the producer's incoming exchanges" do + permissions.order_cycle_exchanges(oc).should == [] + end end # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required - it "returns outgoing exchanges which include variants produced by a producer I manage" do - product = create(:product, supplier: producer) - variant = create(:variant, product: product) - ex.variants << variant - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + describe "legacy compatability" do + context "where my hub's outgoing exchange contains variants of a producer I don't manage and has not given my hub P-OC" do + let!(:product) { create(:product, supplier: producer) } + let!(:variant) { create(:variant, product: product) } + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: true) } + before { ex_out.variants << variant } - permissions.order_cycle_exchanges(oc).should == [ex] + it "returns incoming exchanges supplying the variants in my outgoing exchange" do + permissions.order_cycle_exchanges(oc).should include ex_out + end + end + end + end + + describe "as the manager of a producer" do + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + end + + context "where my producer supplies to the order cycle" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } + + it "returns my producer's incoming exchange" do + permissions.order_cycle_exchanges(oc).should == [ex_in] + end + + context "my producer has granted P-OC to an outgoing hub" do + before do + create(:enterprise_relationship, parent: producer, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns the hub's outgoing exchange" do + permissions.order_cycle_exchanges(oc).should include ex_out + end + end + + context "my producer has not granted P-OC to an outgoing hub" do + it "does not return the hub's outgoing exchange" do + permissions.order_cycle_exchanges(oc).should_not include ex_out + end + end + end + + context "where my producer doesn't supply the order cycle" do + it "does not return the hub's outgoing exchanges" do + permissions.order_cycle_exchanges(oc).should == [] + end + end + + # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required + describe "legacy compatability" do + context "where an outgoing exchange contains variants of a producer I manage" do + let!(:product) { create(:product, supplier: producer) } + let!(:variant) { create(:variant, product: product) } + before { ex_out.variants << variant } + + context "where my producer supplies to the order cycle" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } + + it "returns the outgoing exchange" do + permissions.order_cycle_exchanges(oc).should include ex_out + end + end + + context "where my producer doesn't supply to the order cycle" do + it "does not return the outgoing exchange" do + permissions.order_cycle_exchanges(oc).should_not include ex_out + end + end + end end end end @@ -332,7 +401,7 @@ module OpenFoodNetwork end it "returns all variants belonging to the sending producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1) + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) expect(visible).to include v1 expect(visible).to_not include v2 end @@ -344,7 +413,7 @@ module OpenFoodNetwork end it "returns all variants belonging to the sending producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1) + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) expect(visible).to include v1 expect(visible).to_not include v2 end @@ -356,9 +425,23 @@ module OpenFoodNetwork create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) end - it "returns no variants" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1) - expect(visible).to eq [] + context "where the hub is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + + it "returns variants produced by that producer only" do + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return variants produced by that producer" do + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) + expect(visible).to_not include v1, v2 + end end end end @@ -406,7 +489,7 @@ module OpenFoodNetwork end end - context "as manager of the outgoing hub" do + context "as manager of an outgoing hub" do before do permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) @@ -432,20 +515,33 @@ module OpenFoodNetwork end end - context "as the manager of a producer which has granted P-OC to the outgoing hub" do + context "as the manager of a producer which has granted P-OC to an outgoing hub" do before do permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) end - it "returns all of my produced variants" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 + context "where my producer is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: e1, incoming: true) } + + it "returns all of my produced variants" do + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where my producer isn't in the order cycle" do + # No incoming exchange + + it "does not return my variants" do + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to_not include v1, v2 + end end end - context "as the manager of a producer which has not granted P-OC to the outgoing hub" do + context "as the manager of a producer which has not granted P-OC to an outgoing hub" do before do permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) @@ -457,7 +553,7 @@ module OpenFoodNetwork end # TODO: for backwards compatability, remove later - context "as the manager of a producer which has not granted P-OC to the outgoing hub, but which has variants already in the exchange" do + context "as the manager of a producer which has not granted P-OC to an outgoing hub, but which has variants already in the exchange" do let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } # This one won't be in the exchange, and so shouldn't be visible From bf87394a765fcb3a8ed3547ea4c056e85c21c974 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 5 Apr 2015 10:58:06 +1000 Subject: [PATCH 303/384] Fixing order cycle feature spec --- spec/features/admin/order_cycles_spec.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 95454934dc..b9084aaa82 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -669,7 +669,7 @@ feature %q{ end end - context "that is the manager of a participating distributor" do + context "that is the manager of a participating hub" do let(:my_distributor) { create(:distributor_enterprise) } before do @@ -679,16 +679,19 @@ feature %q{ login_to_admin_as @new_user end - scenario "editing an order cycle we can see only exchanges involving my producer, and any distributors of my products" do + scenario "editing an order cycle we can see only exchanges involving my hub, and any suppliers of variants in my outoing exchanges" do oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [my_distributor, distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) + v1 = create(:variant, product: create(:product, supplier: supplier_managed) ) + ex = oc.exchanges.where(sender_id: distributor_managed, receiver_id: my_distributor, incoming: false).first + ex.update_attributes(variant_ids: [v1.id]) visit edit_admin_order_cycle_path(oc) - # I should only see exchanges for my_distributor - page.all('tr.supplier').count.should == 0 + # I should see exchanges for my_distributor, and the incoming exchange suppling v1 + page.all('tr.supplier').count.should == 1 page.all('tr.distributor').count.should == 1 - # When I save, any exchange that I can't manage remain + # When I save, any exchange that I can't manage remains click_button 'Update' page.should have_content "Your order cycle has been updated." From 009b25a4915a084fb9485caf1e5337f7570b5c48 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 5 Apr 2015 13:59:00 +1000 Subject: [PATCH 304/384] Adding an editable variants scope for incoming exchanges --- .../services/order_cycle.js.coffee | 1 + .../api/admin/order_cycle_serializer.rb | 14 ++++++ ..._exchange_supplied_products_form.html.haml | 9 ++-- lib/open_food_network/permissions.rb | 11 ++++ spec/features/admin/order_cycles_spec.rb | 34 ++++++++++--- .../lib/open_food_network/permissions_spec.rb | 50 +++++++++++++++++++ 6 files changed, 109 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee index b93512c3bb..4e8d462a35 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee @@ -162,6 +162,7 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) stripNonSubmittableAttributes: (order_cycle) -> delete order_cycle.id + delete order_cycle.editable_variants_for_incoming_exchanges delete order_cycle.visible_variants_for_outgoing_exchanges order_cycle diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 68d67e4bb4..6ad7b6b175 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -1,5 +1,6 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges + attributes :editable_variants_for_incoming_exchanges attributes :visible_variants_for_outgoing_exchanges has_many :coordinator_fees, serializer: Api::IdSerializer @@ -17,6 +18,19 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer ActiveModel::ArraySerializer.new(scoped_exchanges, {each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user] }) end + def editable_variants_for_incoming_exchanges + # For each enterprise that the current user is able to see in this order cycle, + # work out which variants should be editable within incoming exchanges from that enterprise + editable = {} + permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: object) + enterprises.each do |enterprise| + variants = permissions.editable_variants_for_incoming_exchanges_between(enterprise, object.coordinator, order_cycle: object).pluck(:id) + editable[enterprise.id] = variants if variants.any? + end + editable + end + def visible_variants_for_outgoing_exchanges # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be visible within outgoing exchanges from that enterprise diff --git a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml index a23c0bc4ba..869a862a1b 100644 --- a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml @@ -11,7 +11,8 @@ .exchange-product-details %label - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-disabled' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', + 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' %img{'ng-src' => '{{ product.image_url }}'} {{ product.name }} @@ -19,10 +20,12 @@ -# be able to remove the master variant, since it serves no purpose. Display a checkbox to do so. .exchange-product-variant{'ng-show' => 'exchange.variants[product.master_id] && product.variants.length > 0'} %label - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', + 'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' Obsolete master .exchange-product-variant{'ng-repeat' => 'variant in product.variants'} %label - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'ofn-sync-distributions' => '{{ variant.id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}' + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'ofn-sync-distributions' => '{{ variant.id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', + 'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' {{ variant.label }} diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 86c94c31b7..483f7bb652 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -146,6 +146,17 @@ module OpenFoodNetwork end end + # Find the variants that a user can POTENTIALLY see within incoming exchanges + def editable_variants_for_incoming_exchanges_between(producer, coordinator, options={}) + return Spree::Variant.where("1=0") unless options[:order_cycle] + if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) + # All variants belonging to the producer + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + Spree::Variant.where("1=0") + end + end + # Find the variants that a user is permitted see within outgoing exchanges # Note that this does not determine whether they actually appear in outgoing exchanges # as this requires first that the variant is included in an incoming exchange diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index b9084aaa82..d5d63799dc 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -644,7 +644,7 @@ feature %q{ login_to_admin_as @new_user end - scenario "editing an order cycle we can see only exchanges involving my producer, and any distributors of my products" do + scenario "editing an order cycle" do oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) v1 = create(:variant, product: create(:product, supplier: supplier_managed) ) ex = oc.exchanges.where(sender_id: distributor_managed, receiver_id: distributor_unmanaged, incoming: false).first @@ -671,26 +671,46 @@ feature %q{ context "that is the manager of a participating hub" do let(:my_distributor) { create(:distributor_enterprise) } + let(:new_user) { create_enterprise_user } before do - @new_user = create_enterprise_user - @new_user.enterprise_roles.build(enterprise: my_distributor).save + create(:enterprise_relationship, parent: supplier_managed, child: my_distributor, permissions_list: [:add_to_order_cycle]) - login_to_admin_as @new_user + new_user.enterprise_roles.build(enterprise: my_distributor).save + login_to_admin_as new_user end - scenario "editing an order cycle we can see only exchanges involving my hub, and any suppliers of variants in my outoing exchanges" do + scenario "editing an order cycle" do oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [my_distributor, distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) v1 = create(:variant, product: create(:product, supplier: supplier_managed) ) + v2 = create(:variant, product: create(:product, supplier: supplier_managed) ) ex = oc.exchanges.where(sender_id: distributor_managed, receiver_id: my_distributor, incoming: false).first - ex.update_attributes(variant_ids: [v1.id]) + ex.update_attributes(variant_ids: [v1.id, v2.id]) + + # # Stub editable_variants_for_incoming_exchanges method so we can test permissions + serializer = Api::Admin::OrderCycleSerializer.new(oc, current_user: new_user) + allow(Api::Admin::OrderCycleSerializer).to receive(:new) { serializer } + allow(serializer).to receive(:editable_variants_for_incoming_exchanges) do + { "#{supplier_managed.id}" => [v1.id] } + end visit edit_admin_order_cycle_path(oc) - # I should see exchanges for my_distributor, and the incoming exchange suppling v1 + # I should see exchanges for my_distributor, and the incoming exchanges supplying the variants in it page.all('tr.supplier').count.should == 1 page.all('tr.distributor').count.should == 1 + # Open the products list for managed_supplier's incoming exchange + within "tr.supplier-#{supplier_managed.id}" do + page.find("td.products input").click + end + + # I should be able to see and toggle v1 + expect(page).to have_field "order_cycle_incoming_exchange_0_variants_#{v1.id}", disabled: false + + # I should be able to see but not toggle v2, because I don't have permission + expect(page).to have_field "order_cycle_incoming_exchange_0_variants_#{v2.id}", disabled: true + # When I save, any exchange that I can't manage remains click_button 'Update' page.should have_content "Your order cycle has been updated." diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index f1e21837e8..0a715a720d 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -574,6 +574,56 @@ module OpenFoodNetwork end end + describe "finding the variants within a hypothetical exchange between two enterprises which are editable by a user" do + let!(:producer1) { create(:supplier_enterprise) } + let!(:producer2) { create(:supplier_enterprise) } + let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } + let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } + let(:oc) { create(:simple_order_cycle) } + + describe "incoming exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of a hub which has been granted P-OC by the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + + it "does not return variants produced by that producer" do + visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) + expect(visible).to_not include v1, v2 + end + end + end + end + end + describe "finding managed products" do let!(:p1) { create(:simple_product) } let!(:p2) { create(:simple_product) } From 136d36113436195bc4fad70e1ef3a336177bfa8e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 5 Apr 2015 14:14:47 +1000 Subject: [PATCH 305/384] Fixing comment --- lib/open_food_network/permissions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 483f7bb652..bc3fb99019 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -146,7 +146,7 @@ module OpenFoodNetwork end end - # Find the variants that a user can POTENTIALLY see within incoming exchanges + # Find the variants that a user can edit within incoming exchanges def editable_variants_for_incoming_exchanges_between(producer, coordinator, options={}) return Spree::Variant.where("1=0") unless options[:order_cycle] if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) From f20844c2a0853d2f1368e1b80354e57389e96c01 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 5 Apr 2015 15:25:44 +1000 Subject: [PATCH 306/384] Adding editable scope for variants in outgoing exchanges --- lib/open_food_network/permissions.rb | 39 +++++ .../lib/open_food_network/permissions_spec.rb | 164 ++++++++++++++++-- 2 files changed, 187 insertions(+), 16 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index bc3fb99019..6284099845 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -197,6 +197,45 @@ module OpenFoodNetwork end end + # Find the variants that a user is permitted edit within outgoing exchanges + def editable_variants_for_outgoing_exchanges_between(coordinator, hub, options={}) + return Spree::Variant.where("1=0") unless options[:order_cycle] + if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(coordinator.id) + # Any variants produced by the coordinator, for outgoing exchanges with itself + coordinator_variants = [] + if hub == coordinator + coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', coordinator) + end + + # Any variants of any producers that have granted the hub P-OC + producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = [] + options[:order_cycle].exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| + active_variants = exchange.variants + end + + Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) + else + # Any of my managed producers in this order cycle granted P-OC by the hub + granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_producers_in(options[:order_cycle])) + + # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub + granting_producers = granting(:add_to_order_cycle, to: [hub], scope: granted_producers) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', granting_producers) + + # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = Spree::Variant.joins(:exchanges, :product). + where('exchanges.receiver_id = (?) AND spree_products.supplier_id IN (?)', hub, managed_enterprises.is_primary_producer) + + Spree::Variant.where(id: permitted_variants | active_variants) + end + end + def managed_products managed_enterprise_products_ids = managed_enterprise_products.pluck :id permitted_enterprise_products_ids = related_enterprise_products.pluck :id diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 0a715a720d..bfadbd29b6 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -550,25 +550,20 @@ module OpenFoodNetwork it "returns an empty array" do expect(permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc)).to eq [] end - end - # TODO: for backwards compatability, remove later - context "as the manager of a producer which has not granted P-OC to an outgoing hub, but which has variants already in the exchange" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + # TODO: for backwards compatability, remove later + context "but which has variants already in the exchange" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + # This one won't be in the exchange, and so shouldn't be visible + let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } - # This one won't be in the exchange, and so shouldn't be visible - let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + before { ex.variants << v2 } - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - ex.variants << v2 - end - - it "returns those variants that are in the exchange" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v3 - expect(visible).to include v2 + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to_not include v1, v3 + expect(visible).to include v2 + end end end end @@ -622,6 +617,143 @@ module OpenFoodNetwork end end end + + describe "outgoing exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + context "where the coordinator produces products" do + let!(:v3) { create(:variant, product: create(:simple_product, supplier: e1)) } + + it "returns any variants produced by the coordinator itself for exchanges with 'self'" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e1, order_cycle: oc) + expect(visible).to include v3 + expect(visible).to_not include v1, v2 + end + + it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2, v3 + end + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to e2 (or e1 for that matter) + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1, v2 + end + end + end + + context "as manager of an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to e2 + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1, v2 + end + end + end + + context "as the manager of a producer which has granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + context "where my producer is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: e1, incoming: true) } + + context "where the outgoing hub has granted P-OC to my producer" do + before do + create(:enterprise_relationship, parent: e2, child: producer1, permissions_list: [:add_to_order_cycle]) + end + + it "returns all of my produced variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where the outgoing hub has not granted P-OC to my producer" do + # No permission granted + + it "does not return my variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to_not include v1, v2 + end + end + end + + context "where my producer isn't in the order cycle" do + # No incoming exchange + + it "does not return my variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to_not include v1, v2 + end + end + end + + context "as the manager of a producer which has not granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } + create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) + end + + it "returns an empty array" do + expect(permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc)).to eq [] + end + + # TODO: for backwards compatability, remove later + context "but which has variants already in the exchange" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + # This one won't be in the exchange, and so shouldn't be visible + let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) + expect(visible).to_not include v1, v3 + expect(visible).to include v2 + end + end + end + end end describe "finding managed products" do From a4b80b1f330ecff4114ae36e22cf831f249788b3 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 09:15:14 +1000 Subject: [PATCH 307/384] Implement editable scope for variants on order cycles page --- .../services/order_cycle.js.coffee | 1 + .../api/admin/order_cycle_serializer.rb | 15 ++++++- ...change_distributed_products_form.html.haml | 6 ++- spec/features/admin/order_cycles_spec.rb | 41 +++++++++++++++---- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee index 4e8d462a35..98b685766e 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee @@ -163,6 +163,7 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) stripNonSubmittableAttributes: (order_cycle) -> delete order_cycle.id delete order_cycle.editable_variants_for_incoming_exchanges + delete order_cycle.editable_variants_for_outgoing_exchanges delete order_cycle.visible_variants_for_outgoing_exchanges order_cycle diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 6ad7b6b175..53976ee09a 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -1,6 +1,6 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges - attributes :editable_variants_for_incoming_exchanges + attributes :editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges attributes :visible_variants_for_outgoing_exchanges has_many :coordinator_fees, serializer: Api::IdSerializer @@ -31,6 +31,19 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer editable end + def editable_variants_for_outgoing_exchanges + # For each enterprise that the current user is able to see in this order cycle, + # work out which variants should be editable within incoming exchanges from that enterprise + editable = {} + permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) + enterprises = permissions.order_cycle_enterprises_for(order_cycle: object) + enterprises.each do |enterprise| + variants = permissions.editable_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) + editable[enterprise.id] = variants if variants.any? + end + editable + end + def visible_variants_for_outgoing_exchanges # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be visible within outgoing exchanges from that enterprise diff --git a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml index efdf3bdf35..5197242d8a 100644 --- a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml @@ -9,12 +9,14 @@ .exchange-product-details .supplier {{ product.supplier_name }} %label - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-disabled' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', + 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' %img{'ng-src' => '{{ product.image_url }}'} {{ product.name }} -# if we ever need to filter variants within a product using visibility permissions, we can use this filter: visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges .exchange-product-variant{'ng-repeat' => 'variant in product.variants | filter:variantSuppliedToOrderCycle'} %label - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}' + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', + 'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' {{ variant.label }} diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index d5d63799dc..2d6a854d5c 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -637,18 +637,32 @@ feature %q{ end context "that is a manager of a participating producer" do - before do - @new_user = create_enterprise_user - @new_user.enterprise_roles.build(enterprise: supplier_managed).save + let(:new_user) { create_enterprise_user } - login_to_admin_as @new_user + before do + new_user.enterprise_roles.build(enterprise: supplier_managed).save + login_to_admin_as new_user end scenario "editing an order cycle" do oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) v1 = create(:variant, product: create(:product, supplier: supplier_managed) ) - ex = oc.exchanges.where(sender_id: distributor_managed, receiver_id: distributor_unmanaged, incoming: false).first - ex.update_attributes(variant_ids: [v1.id]) + v2 = create(:variant, product: create(:product, supplier: supplier_managed) ) + + # Incoming exchange + ex_in = oc.exchanges.where(sender_id: supplier_managed, receiver_id: distributor_managed, incoming: true).first + ex_in.update_attributes(variant_ids: [v1.id, v2.id]) + + # Outgoing exchange + ex_out = oc.exchanges.where(sender_id: distributor_managed, receiver_id: distributor_managed, incoming: false).first + ex_out.update_attributes(variant_ids: [v1.id, v2.id]) + + # Stub editable_variants_for_outgoing_exchanges method so we can test permissions + serializer = Api::Admin::OrderCycleSerializer.new(oc, current_user: new_user) + allow(Api::Admin::OrderCycleSerializer).to receive(:new) { serializer } + allow(serializer).to receive(:editable_variants_for_outgoing_exchanges) do + { "#{distributor_managed.id}" => [v1.id] } + end visit edit_admin_order_cycle_path(oc) @@ -656,7 +670,18 @@ feature %q{ # distributor_managed and distributor_permitted (who I have given permission to) AND # and distributor_unmanaged (who distributes my products) page.all('tr.supplier').count.should == 1 - page.all('tr.distributor').count.should == 3 + page.all('tr.distributor').count.should == 2 + + # Open the products list for managed_supplier's incoming exchange + within "tr.distributor-#{distributor_managed.id}" do + page.find("td.products input").click + end + + # I should be able to see and toggle v1 + expect(page).to have_field "order_cycle_outgoing_exchange_0_variants_#{v1.id}", disabled: false + + # I should be able to see but not toggle v2, because I don't have permission + expect(page).to have_field "order_cycle_outgoing_exchange_0_variants_#{v2.id}", disabled: true # When I save, any exchanges that I can't manage remain click_button 'Update' @@ -687,7 +712,7 @@ feature %q{ ex = oc.exchanges.where(sender_id: distributor_managed, receiver_id: my_distributor, incoming: false).first ex.update_attributes(variant_ids: [v1.id, v2.id]) - # # Stub editable_variants_for_incoming_exchanges method so we can test permissions + # Stub editable_variants_for_incoming_exchanges method so we can test permissions serializer = Api::Admin::OrderCycleSerializer.new(oc, current_user: new_user) allow(Api::Admin::OrderCycleSerializer).to receive(:new) { serializer } allow(serializer).to receive(:editable_variants_for_incoming_exchanges) do From 52dc313e700dc976aeafb4d6fc980955543bd186 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 09:20:39 +1000 Subject: [PATCH 308/384] Implementing editable variant scopes in order_cycle_applicator --- lib/open_food_network/order_cycle_form_applicator.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index 8f36a367ac..a5ed5fefea 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -105,17 +105,15 @@ module OpenFoodNetwork @user_manages_coordinator = Enterprise.managed_by(@spree_current_user).include? @order_cycle.coordinator end - # TODO Need to use editable rather than visible def editable_variant_ids_for_incoming_exchange_between(sender, receiver) OpenFoodNetwork::Permissions.new(@spree_current_user). - visible_variants_for_incoming_exchanges_between(sender, receiver, order_cycle: @order_cycle). + editable_variants_for_incoming_exchanges_between(sender, receiver, order_cycle: @order_cycle). pluck(:id) end - # TODO Need to use editable rather than visible def editable_variant_ids_for_outgoing_exchange_between(sender, receiver) OpenFoodNetwork::Permissions.new(@spree_current_user). - visible_variants_for_outgoing_exchanges_between(sender, receiver, order_cycle: @order_cycle). + editable_variants_for_outgoing_exchanges_between(sender, receiver, order_cycle: @order_cycle). pluck(:id) end From b7de8353bcf0d921278e248c37541e83e684f2b7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 09:47:37 +1000 Subject: [PATCH 309/384] Renaming existing enterprise fee serializer as basic... --- app/serializers/api/admin/basic_enterprise_fee_serializer.rb | 3 +++ app/serializers/api/admin/enterprise_fee_serializer.rb | 3 --- app/serializers/api/admin/exchange_serializer.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 app/serializers/api/admin/basic_enterprise_fee_serializer.rb delete mode 100644 app/serializers/api/admin/enterprise_fee_serializer.rb diff --git a/app/serializers/api/admin/basic_enterprise_fee_serializer.rb b/app/serializers/api/admin/basic_enterprise_fee_serializer.rb new file mode 100644 index 0000000000..07bcaffc0c --- /dev/null +++ b/app/serializers/api/admin/basic_enterprise_fee_serializer.rb @@ -0,0 +1,3 @@ +class Api::Admin::BasicEnterpriseFeeSerializer < ActiveModel::Serializer + attributes :id, :enterprise_id +end diff --git a/app/serializers/api/admin/enterprise_fee_serializer.rb b/app/serializers/api/admin/enterprise_fee_serializer.rb deleted file mode 100644 index 5f5755507e..0000000000 --- a/app/serializers/api/admin/enterprise_fee_serializer.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer - attributes :id, :enterprise_id -end diff --git a/app/serializers/api/admin/exchange_serializer.rb b/app/serializers/api/admin/exchange_serializer.rb index 4fa7760de5..518d865003 100644 --- a/app/serializers/api/admin/exchange_serializer.rb +++ b/app/serializers/api/admin/exchange_serializer.rb @@ -1,7 +1,7 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer attributes :id, :sender_id, :receiver_id, :incoming, :variants, :pickup_time, :pickup_instructions - has_many :enterprise_fees, serializer: Api::Admin::EnterpriseFeeSerializer + has_many :enterprise_fees, serializer: Api::Admin::BasicEnterpriseFeeSerializer def variants permitted = Spree::Variant.where("1=0") From 7ebea23fda194dffcc71d9282dc517745d47abfc Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 10:23:08 +1000 Subject: [PATCH 310/384] Adding route for now for_order_cycle action on enterprise fees --- config/routes.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index a7fe216d64..4621ee4a35 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -65,7 +65,10 @@ Openfoodnetwork::Application.routes.draw do resources :enterprise_roles resources :enterprise_fees do - post :bulk_update, :on => :collection, :as => :bulk_update + collection do + get :for_order_cycle + post :bulk_update, :as => :bulk_update + end end resources :enterprise_groups do From 7448d41b27c36a7081df9a07ddc60ab43325be9d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 10:23:39 +1000 Subject: [PATCH 311/384] Adding abilities for new for_order_cycle action --- app/models/spree/ability_decorator.rb | 1 + spec/models/spree/ability_spec.rb | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 09d46c523e..e66c03aa72 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -131,6 +131,7 @@ class AbilityDecorator user.enterprises.include? order_cycle.coordinator end can [:for_order_cycle], Enterprise + can [:for_order_cycle], EnterpriseFee end def add_order_management_abilities(user) diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index bb45487894..0dc9364967 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -235,6 +235,10 @@ module Spree it "cannot request permitted enterprises for an order cycle" do should_not have_ability([:for_order_cycle], for: Enterprise) end + + it "cannot request permitted enterprise fees for an order cycle" do + should_not have_ability([:for_order_cycle], for: EnterpriseFee) + end end context "where the enterprise is in an order_cycle" do @@ -252,6 +256,10 @@ module Spree it "can request permitted enterprises for an order cycle" do should have_ability([:for_order_cycle], for: Enterprise) end + + it "can request permitted enterprise fees for an order cycle" do + should have_ability([:for_order_cycle], for: EnterpriseFee) + end end end end @@ -415,6 +423,10 @@ module Spree it "can request permitted enterprises for an order cycle" do should have_ability([:for_order_cycle], for: Enterprise) end + + it "can request permitted enterprise fees for an order cycle" do + should have_ability([:for_order_cycle], for: EnterpriseFee) + end end context 'Order Cycle co-ordinator, distributor enterprise manager' do @@ -441,7 +453,7 @@ module Spree end it "should be able to read/write EnterpriseFees" do - should have_ability([:admin, :index, :read, :create, :edit, :bulk_update, :destroy], for: EnterpriseFee) + should have_ability([:admin, :index, :read, :create, :edit, :bulk_update, :destroy, :for_order_cycle], for: EnterpriseFee) end it "should be able to add enterprises to order cycles" do From 7eb735f87e1b1c7dee657012d2c1d2421431850d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 10:24:40 +1000 Subject: [PATCH 312/384] Adding for_enterprises scope on enterprise fee --- app/models/enterprise_fee.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 730bac0787..e19f91d0f4 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -20,6 +20,7 @@ class EnterpriseFee < ActiveRecord::Base scope :for_enterprise, lambda { |enterprise| where(enterprise_id: enterprise) } + scope :for_enterprises, lambda { |enterprises| where(enterprise_id: enterprises) } scope :managed_by, lambda { |user| if user.has_spree_role?('admin') From d516795774af7c4a715f794d1a66eeb71dd01a3b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 13:06:21 +1000 Subject: [PATCH 313/384] Fetching Enterprise Fees for order cycle, using new enterprisefee serializer --- .../admin/order_cycle.js.erb.coffee | 4 +-- .../controllers/simple_create.js.coffee | 2 +- .../controllers/simple_edit.js.coffee | 2 +- .../services/enterprise_fee.js.coffee | 14 ++++++--- .../admin/enterprise_fees_controller.rb | 30 +++++++++++++++++-- .../api/admin/enterprise_fee_serializer.rb | 23 ++++++++++++++ .../unit/order_cycle_spec.js.coffee | 2 +- 7 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 app/serializers/api/admin/enterprise_fee_serializer.rb diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 21acdc108d..7f8f9ae087 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -2,7 +2,7 @@ angular.module('admin.order_cycles', ['ngResource']) .controller('AdminCreateOrderCycleCtrl', ['$scope', 'OrderCycle', 'Enterprise', 'EnterpriseFee', 'ocInstance', ($scope, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> $scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id) $scope.supplied_products = Enterprise.supplied_products - $scope.enterprise_fees = EnterpriseFee.index() + $scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id) $scope.order_cycle = OrderCycle.new({ coordinator_id: ocInstance.coordinator_id}) @@ -83,7 +83,7 @@ angular.module('admin.order_cycles', ['ngResource']) order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] $scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id) $scope.supplied_products = Enterprise.supplied_products - $scope.enterprise_fees = EnterpriseFee.index() + $scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: order_cycle_id) $scope.order_cycle = OrderCycle.load(order_cycle_id) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee index abc26271a1..771902ca5b 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee @@ -3,7 +3,7 @@ angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl # TODO: make this a get method, which only fetches one enterprise $scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) => $scope.init(enterprises) - $scope.enterprise_fees = EnterpriseFee.index() + $scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id) $scope.init = (enterprises) -> enterprise = enterprises[Object.keys(enterprises)[0]] diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee index fdc15e220c..cab677407e 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee @@ -3,7 +3,7 @@ angular.module('admin.order_cycles').controller "AdminSimpleEditOrderCycleCtrl", $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] $scope.enterprises = Enterprise.index(order_cycle_id: $scope.orderCycleId()) - $scope.enterprise_fees = EnterpriseFee.index() + $scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: $scope.orderCycleId()) $scope.order_cycle = OrderCycle.load $scope.orderCycleId(), (order_cycle) => $scope.init() diff --git a/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee b/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee index 330d7c031e..c9d4d50b5b 100644 --- a/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee @@ -1,18 +1,24 @@ angular.module('admin.order_cycles').factory('EnterpriseFee', ($resource) -> - EnterpriseFee = $resource('/admin/enterprise_fees/:enterprise_fee_id.json', {}, {'index': {method: 'GET', isArray: true}}) + EnterpriseFee = $resource('/admin/enterprise_fees/for_order_cycle/:enterprise_fee_id.json', {}, { + 'index': + method: 'GET' + isArray: true + params: + order_cycle_id: '@order_cycle_id' + coordinator_id: '@coordinator_id' + }) { EnterpriseFee: EnterpriseFee enterprise_fees: {} loaded: false - index: -> + index: (params={}) -> service = this - EnterpriseFee.index (data) -> + EnterpriseFee.index params, (data) -> service.enterprise_fees = data service.loaded = true forEnterprise: (enterprise_id) -> enterprise_fee for enterprise_fee in this.enterprise_fees when enterprise_fee.enterprise_id == enterprise_id }) - diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index c4d4a621b1..016f697856 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -20,6 +20,17 @@ module Admin end end + def for_order_cycle + respond_to do |format| + format.html + format.json do + render json: ActiveModel::ArraySerializer.new( @collection, + each_serializer: Api::Admin::EnterpriseFeeSerializer, controller: self + ).to_json + end + end + end + def bulk_update @enterprise_fee_set = EnterpriseFeeSet.new(params[:enterprise_fee_set]) if @enterprise_fee_set.save @@ -63,9 +74,22 @@ module Admin end def collection - collection = EnterpriseFee.managed_by(spree_current_user).order('enterprise_id', 'fee_type', 'name') - collection = collection.for_enterprise(current_enterprise) if current_enterprise - collection + case action + when :for_order_cycle + options = {} + options[:coordinator] = Enterprise.find(params[:coordinator_id]) if params[:coordinator_id] + options[:order_cycle] = OrderCycle.find(params[:order_cycle_id]) if params[:order_cycle_id] + enterprises = OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(options) + return EnterpriseFee.for_enterprises(enterprises).order('enterprise_id', 'fee_type', 'name') + else + collection = EnterpriseFee.managed_by(spree_current_user).order('enterprise_id', 'fee_type', 'name') + collection = collection.for_enterprise(current_enterprise) if current_enterprise + collection + end + end + + def collection_actions + [:index, :for_order_cycle] end def current_enterprise diff --git a/app/serializers/api/admin/enterprise_fee_serializer.rb b/app/serializers/api/admin/enterprise_fee_serializer.rb new file mode 100644 index 0000000000..1b5a201b65 --- /dev/null +++ b/app/serializers/api/admin/enterprise_fee_serializer.rb @@ -0,0 +1,23 @@ +class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer + attributes :id, :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type + attributes :enterprise_name, :calculator_description, :calculator_settings + + def enterprise_name + object.enterprise.andand.name + end + + def calculator_description + object.calculator.andand.description + end + + def calculator_settings + result = nil + + options[:controller].send(:with_format, :html) do + result = options[:controller].render_to_string :partial => 'admin/enterprise_fees/calculator_settings', :locals => {:enterprise_fee => object} + end + + result.gsub('[0]', '[{{ $index }}]').gsub('_0_', '_{{ $index }}_') + end + +end diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index d294475acb..52a89e8ac4 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -396,7 +396,7 @@ describe 'OrderCycle services', -> inject ($injector, _$httpBackend_)-> EnterpriseFee = $injector.get('EnterpriseFee') $httpBackend = _$httpBackend_ - $httpBackend.whenGET('/admin/enterprise_fees.json').respond [ + $httpBackend.whenGET('/admin/enterprise_fees/for_order_cycle.json?').respond [ {id: 1, name: "Yayfee", enterprise_id: 1} {id: 2, name: "FeeTwo", enterprise_id: 2} ] From 36c5cdf556af1c530f81d7aa8486db46d1fd1952 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 14:08:10 +1000 Subject: [PATCH 314/384] Only show enterprises with fees in order cycle edit page --- .../javascripts/admin/order_cycle.js.erb.coffee | 8 ++++---- .../order_cycles/services/enterprise_fee.js.coffee | 9 ++++----- .../admin/order_cycles/_exchange_form.html.haml | 2 +- spec/javascripts/unit/order_cycle_spec.js.coffee | 12 ++++++++---- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 7f8f9ae087..298ddcc374 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -33,8 +33,8 @@ angular.module('admin.order_cycles', ['ngResource']) $scope.exchangeDirection = (exchange) -> OrderCycle.exchangeDirection(exchange) - $scope.participatingEnterprises = -> - $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() + $scope.enterprisesWithFees = -> + $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 1 $scope.toggleProducts = ($event, exchange) -> $event.preventDefault() @@ -114,8 +114,8 @@ angular.module('admin.order_cycles', ['ngResource']) $scope.exchangeDirection = (exchange) -> OrderCycle.exchangeDirection(exchange) - $scope.participatingEnterprises = -> - $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() + $scope.enterprisesWithFees = -> + $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 1 $scope.toggleProducts = ($event, exchange) -> $event.preventDefault() diff --git a/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee b/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee index c9d4d50b5b..fe443a7cf5 100644 --- a/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee @@ -14,11 +14,10 @@ angular.module('admin.order_cycles').factory('EnterpriseFee', ($resource) -> loaded: false index: (params={}) -> - service = this - EnterpriseFee.index params, (data) -> - service.enterprise_fees = data - service.loaded = true + EnterpriseFee.index params, (data) => + @enterprise_fees = data + @loaded = true forEnterprise: (enterprise_id) -> - enterprise_fee for enterprise_fee in this.enterprise_fees when enterprise_fee.enterprise_id == enterprise_id + enterprise_fee for enterprise_fee in @enterprise_fees when enterprise_fee.enterprise_id == enterprise_id }) diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index cdd6bfbcd5..d37722a910 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -15,7 +15,7 @@ %td.fees %ol %li{'ng-repeat' => 'enterprise_fee in exchange.enterprise_fees'} - = select_tag 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', nil, {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', 'ng-model' => 'enterprise_fee.enterprise_id', 'ng-options' => 'enterprise.id as enterprise.name for enterprise in participatingEnterprises()'} + = select_tag 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', nil, {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', 'ng-model' => 'enterprise_fee.enterprise_id', 'ng-options' => 'enterprise.id as enterprise.name for enterprise in enterprisesWithFees()'} = select_tag 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_fee_id', nil, {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_fee_id', 'ng-model' => 'enterprise_fee.id', 'ng-options' => 'enterprise_fee.id as enterprise_fee.name for enterprise_fee in enterpriseFeesForEnterprise(enterprise_fee.enterprise_id)'} diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 52a89e8ac4..bee45ae028 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -94,12 +94,14 @@ describe 'OrderCycle controllers', -> expect(scope.exchangeDirection('exchange')).toEqual('exchange direction') expect(OrderCycle.exchangeDirection).toHaveBeenCalledWith('exchange') - it 'Finds enterprises participating in the order cycle', -> + it 'Finds enterprises participating in the order cycle that have fees', -> scope.enterprises = 1: {id: 1, name: 'Eaterprises'} 2: {id: 2, name: 'Pepper Tree Place'} + 3: {id: 3, name: 'South East'} OrderCycle.participatingEnterpriseIds = jasmine.createSpy('participatingEnterpriseIds').andReturn([2]) - expect(scope.participatingEnterprises()).toEqual([ + EnterpriseFee.enterprise_fees = [ {enterprise_id: 2} ] # Pepper Tree Place has a fee + expect(scope.enterprisesWithFees()).toEqual([ {id: 2, name: 'Pepper Tree Place'} ]) @@ -255,12 +257,14 @@ describe 'OrderCycle controllers', -> expect(scope.exchangeDirection('exchange')).toEqual('exchange direction') expect(OrderCycle.exchangeDirection).toHaveBeenCalledWith('exchange') - it 'Finds enterprises participating in the order cycle', -> + it 'Finds enterprises participating in the order cycle that have fees', -> scope.enterprises = 1: {id: 1, name: 'Eaterprises'} 2: {id: 2, name: 'Pepper Tree Place'} + 3: {id: 3, name: 'South East'} OrderCycle.participatingEnterpriseIds = jasmine.createSpy('participatingEnterpriseIds').andReturn([2]) - expect(scope.participatingEnterprises()).toEqual([ + EnterpriseFee.enterprise_fees = [ {enterprise_id: 2} ] # Pepper Tree Place has a fee + expect(scope.enterprisesWithFees()).toEqual([ {id: 2, name: 'Pepper Tree Place'} ]) From ae1681b790702367e5946b9e167c27b7ff994930 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 23:29:15 +1000 Subject: [PATCH 315/384] Not 1! Zero! --- app/assets/javascripts/admin/order_cycle.js.erb.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 298ddcc374..64f06e43a8 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -34,7 +34,7 @@ angular.module('admin.order_cycles', ['ngResource']) OrderCycle.exchangeDirection(exchange) $scope.enterprisesWithFees = -> - $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 1 + $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0 $scope.toggleProducts = ($event, exchange) -> $event.preventDefault() @@ -115,7 +115,7 @@ angular.module('admin.order_cycles', ['ngResource']) OrderCycle.exchangeDirection(exchange) $scope.enterprisesWithFees = -> - $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 1 + $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0 $scope.toggleProducts = ($event, exchange) -> $event.preventDefault() From 4a87798bb4c937c22668c9be040232f7cdd7b99c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Mon, 6 Apr 2015 23:30:43 +1000 Subject: [PATCH 316/384] Variants belonging to a producer I manager that are already in an outgoing an exchange need not be editable --- lib/open_food_network/permissions.rb | 15 ++++----------- spec/lib/open_food_network/permissions_spec.rb | 5 ++--- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 6284099845..1de4089f8e 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -186,12 +186,10 @@ module OpenFoodNetwork producers = granting(:add_to_order_cycle, to: [hub], scope: managed_producers_in(options[:order_cycle])) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - # PLUS any of my producers variants that are already in an outgoing exchange of this hub, so things don't break + # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break # TODO: Remove this when all P-OC are sorted out - active_variants = [] - options[:order_cycle].exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| - active_variants = exchange.variants.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) - end + active_variants = Spree::Variant.joins(:exchanges, :product). + where("exchanges.receiver_id = (?) AND spree_products.supplier_id IN (?) AND incoming = 'f'", hub, managed_enterprises.is_primary_producer) Spree::Variant.where(id: permitted_variants | active_variants) end @@ -227,12 +225,7 @@ module OpenFoodNetwork granting_producers = granting(:add_to_order_cycle, to: [hub], scope: granted_producers) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', granting_producers) - # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break - # TODO: Remove this when all P-OC are sorted out - active_variants = Spree::Variant.joins(:exchanges, :product). - where('exchanges.receiver_id = (?) AND spree_products.supplier_id IN (?)', hub, managed_enterprises.is_primary_producer) - - Spree::Variant.where(id: permitted_variants | active_variants) + Spree::Variant.where(id: permitted_variants ) end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index bfadbd29b6..82c4260933 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -746,10 +746,9 @@ module OpenFoodNetwork before { ex.variants << v2 } - it "returns those variants that are in the exchange" do + it "does not return my variants" do visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v3 - expect(visible).to include v2 + expect(visible).to_not include v1, v2, v3 end end end From 24062e5585c3dce7c2a98e93fc0f3753371bb3d7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 10:15:50 +1000 Subject: [PATCH 317/384] Adding permissions for outgoing hubs to see producers whose variants they are distributing, despite P-OC not existing --- lib/open_food_network/permissions.rb | 9 ++++++++- spec/lib/open_food_network/permissions_spec.rb | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 1de4089f8e..0eea2d62f4 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -62,6 +62,7 @@ module OpenFoodNetwork managed_active = [] hubs_active = [] + producers_active = [] if order_cycle # TODO: remove this when permissions are all sorted out # Any enterprises that I manage that are already in the order_cycle @@ -72,9 +73,15 @@ module OpenFoodNetwork variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants) hubs_active = active_exchanges.map(&:receiver_id) + + # TODO: Remove this when all P-OC are sorted out + # Any producers of variants that hubs I manage are currently distributing in this OC + variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", managed_hubs_in(order_cycle), order_cycle).pluck(:id).uniq + products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq + producers_active = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq end - Enterprise.where(id: coordinator_permitted | managed_permitted | managed_active | hubs_permitted | producers_permitted | hubs_active) + Enterprise.where(id: coordinator_permitted | managed_permitted | hubs_permitted | producers_permitted | managed_active | hubs_active | producers_active ) end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 82c4260933..b6ec645e68 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -105,6 +105,18 @@ module OpenFoodNetwork expect(enterprises).to_not include producer, coordinator end end + + context "and distributes variants distributed by an unmanaged and unpermitted producer" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + before { ex_outgoing.variants << create(:variant, product: create(:product, supplier: producer)) } + + # TODO: update this when we are confident about P-OCs + it "returns that producer as well" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end end end From 8859aa27ee9b1a74da78a70ea0e4287ddaf866f7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 10:33:32 +1000 Subject: [PATCH 318/384] Slight refactor of order cycle enterprises --- lib/open_food_network/permissions.rb | 33 +++++++++++++--------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 0eea2d62f4..f20cf38514 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -30,27 +30,22 @@ module OpenFoodNetwork coordinator = order_cycle.coordinator end - - if coordinator.sells == "own" - - # Coordinators that sell own can only see themselves in the OC interface + if managed_enterprises.include? coordinator coordinator_permitted = [] - if managed_enterprises.include? coordinator - coordinator_permitted << coordinator - end - Enterprise.where(id: coordinator_permitted) - else - # If the coordinator sells any, relationships come into play - - # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) - # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) - coordinator_permitted = [] - if managed_enterprises.include? coordinator + if coordinator.sells == "own" + # Coordinators that sell own can only see themselves in the OC interface + coordinator_permitted = [coordinator] + elsif coordinator.sells == "any" + # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) + # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) + # If the coordinator sells any, relationships come into play coordinator_permitted = granting(:add_to_order_cycle, to: [coordinator]).pluck(:id) - coordinator_permitted << coordinator + coordinator_permitted = coordinator_permitted | [coordinator] end + Enterprise.where(id: coordinator_permitted) + else # Any enterprises that I manage directly, which have granted P-OC to the coordinator managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises).pluck(:id) @@ -64,7 +59,7 @@ module OpenFoodNetwork hubs_active = [] producers_active = [] if order_cycle - # TODO: remove this when permissions are all sorted out + # TODO: Remove this when all P-OC are sorted out # Any enterprises that I manage that are already in the order_cycle managed_active = managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors).pluck(:id) @@ -81,7 +76,9 @@ module OpenFoodNetwork producers_active = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq end - Enterprise.where(id: coordinator_permitted | managed_permitted | hubs_permitted | producers_permitted | managed_active | hubs_active | producers_active ) + ids = managed_permitted | hubs_permitted | producers_permitted | managed_active | hubs_active | producers_active + + Enterprise.where(id: ids.sort ) end end From 4dcfec1de9e2c6c7fb658839765abfe0399a9230 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 12:12:02 +1000 Subject: [PATCH 319/384] make enterprises_for_order_cycle permissions more correct --- lib/open_food_network/permissions.rb | 30 ++-- .../lib/open_food_network/permissions_spec.rb | 160 ++++++++++++++---- 2 files changed, 150 insertions(+), 40 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index f20cf38514..fcad0e829b 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -47,13 +47,19 @@ module OpenFoodNetwork Enterprise.where(id: coordinator_permitted) else # Any enterprises that I manage directly, which have granted P-OC to the coordinator - managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises).pluck(:id) + managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises_in(order_cycle) ).pluck(:id) - # Any hubs that have been granted P-OC by producers I manage - hubs_permitted = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer, scope: Enterprise.is_hub).pluck(:id) + # Any hubs in this OC that have been granted P-OC by producers I manage in this OC + hubs_permitted = granted(:add_to_order_cycle, by: managed_producers_in(order_cycle), scope: order_cycle.distributors).pluck(:id) - # Any producers that have granted P-OC to hubs I manage - producers_permitted = granting(:add_to_order_cycle, to: managed_enterprises.is_hub, scope: Enterprise.is_primary_producer).pluck(:id) + # Any hubs in this OC that have granted P-OC to producers I manage in this OC + hubs_permitting = granting(:add_to_order_cycle, to: managed_producers_in(order_cycle), scope: order_cycle.distributors).pluck(:id) + + # Any producers in this OC that have been granted P-OC by hubs I manage in this OC + producers_permitted = granted(:add_to_order_cycle, by: managed_hubs_in(order_cycle), scope: order_cycle.suppliers).pluck(:id) + + # Any producers in this OC that have granted P-OC to hubs I manage in this OC + producers_permitting = granting(:add_to_order_cycle, to: managed_hubs_in(order_cycle), scope: order_cycle.suppliers).pluck(:id) managed_active = [] hubs_active = [] @@ -61,7 +67,7 @@ module OpenFoodNetwork if order_cycle # TODO: Remove this when all P-OC are sorted out # Any enterprises that I manage that are already in the order_cycle - managed_active = managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors).pluck(:id) + managed_active = managed_enterprises_in(order_cycle).pluck(:id) # TODO: Remove this when all P-OC are sorted out # Any hubs that currently have outgoing exchanges distributing variants of producers I manage @@ -76,7 +82,7 @@ module OpenFoodNetwork producers_active = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq end - ids = managed_permitted | hubs_permitted | producers_permitted | managed_active | hubs_active | producers_active + ids = managed_permitted | hubs_permitted | hubs_permitting | producers_permitted | producers_permitting | managed_active | hubs_active | producers_active Enterprise.where(id: ids.sort ) end @@ -261,14 +267,16 @@ module OpenFoodNetwork Enterprise.managed_by(@user) end + def managed_enterprises_in(order_cycle) + managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors) + end + def managed_hubs_in(order_cycle) - Enterprise.with_order_cycles_as_distributor_outer.where("order_cycles.id = (?)", order_cycle.id) - .merge(managed_enterprises.is_hub) + managed_enterprises_in(order_cycle).is_hub end def managed_producers_in(order_cycle) - Enterprise.with_order_cycles_as_supplier_outer.where("order_cycles.id = (?)", order_cycle.id) - .merge(managed_enterprises.is_primary_producer) + managed_enterprises_in(order_cycle).is_primary_producer end def related_enterprises_with(permission) diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index b6ec645e68..adb319d801 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -73,19 +73,70 @@ module OpenFoodNetwork create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) end - it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) - expect(enterprises).to include hub - expect(enterprises).to_not include producer, coordinator + context "where my hub is in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns my hub" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include hub + expect(enterprises).to_not include producer, coordinator + end + + context "and has been granted P-OC by a producer" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where the producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns the producer" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include producer, hub + end + end + + context "where the producer is not in the order cycle" do + # No incoming exchange + + it "does not return the producer" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to_not include producer + end + end + end + + context "and has granted P-OC to a producer" do + before do + create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) + end + + context "where the producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns the producer" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include producer, hub + end + end + + context "where the producer is not in the order cycle" do + # No incoming exchange + + it "does not return the producer" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to_not include producer + end + end + end end - context "and has also granted P-OC to a producer" do - before do - create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) - end - it "does not return that producer" do + context "where my hub is not in the order cycle" do + # No outgoing exchange for my hub + + it "does not return my hub" do enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) - expect(enterprises).to_not include producer + expect(enterprises).to_not include hub, producer, coordinator end end end @@ -104,17 +155,16 @@ module OpenFoodNetwork expect(enterprises).to include hub expect(enterprises).to_not include producer, coordinator end - end - context "and distributes variants distributed by an unmanaged and unpermitted producer" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - before { ex_outgoing.variants << create(:variant, product: create(:product, supplier: producer)) } + context "and distributes variants distributed by an unmanaged and unpermitted producer" do + before { ex.variants << create(:variant, product: create(:product, supplier: producer)) } - # TODO: update this when we are confident about P-OCs - it "returns that producer as well" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator + # TODO: update this when we are confident about P-OCs + it "returns that producer as well" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end end end end @@ -130,20 +180,72 @@ module OpenFoodNetwork create(:enterprise_relationship, parent: producer, child: coordinator, permissions_list: [:add_to_order_cycle]) end - it "returns my producer, and the coordindator itself" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) - expect(enterprises).to include producer, coordinator - expect(enterprises).to_not include hub - end + context "where my producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - context "and has also granted P-OC to a hub" do - before do - create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + it "returns my producer" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include producer + expect(enterprises).to_not include hub, coordinator end - it "returns that hub as well" do + context "and has been granted P-OC by a hub" do + before do + create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is also in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns the hub as well" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return the hub" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to_not include hub + end + end + end + + context "and has granted P-OC to a hub" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is also in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns the hub as well" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return the hub" do + enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + expect(enterprises).to_not include hub + end + end + end + end + + context "where my producer is not in the order cycle" do + # No incoming exchange for producer + + it "does not return my producer" do enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) - expect(enterprises).to include producer, coordinator, hub + expect(enterprises).to_not include hub, producer, coordinator end end end From 104a8ddecfb76b60c1f6fdfbccde6d6622e6f73a Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 13:00:17 +1000 Subject: [PATCH 320/384] Refactor order_cycle_enterpises_for permission method, now requires an order cycle --- .../admin/enterprise_fees_controller.rb | 8 ++-- .../admin/enterprises_controller.rb | 8 ++-- app/helpers/order_cycles_helper.rb | 2 +- .../api/admin/order_cycle_serializer.rb | 6 +-- app/views/admin/order_cycles/_row.html.haml | 4 +- .../order_cycle_form_applicator.rb | 2 +- lib/open_food_network/permissions.rb | 13 ++--- .../admin/enterprises_controller_spec.rb | 19 ++++---- .../lib/open_food_network/permissions_spec.rb | 48 ++++++++++--------- 9 files changed, 54 insertions(+), 56 deletions(-) diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index 016f697856..13e27e05aa 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -76,10 +76,10 @@ module Admin def collection case action when :for_order_cycle - options = {} - options[:coordinator] = Enterprise.find(params[:coordinator_id]) if params[:coordinator_id] - options[:order_cycle] = OrderCycle.find(params[:order_cycle_id]) if params[:order_cycle_id] - enterprises = OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(options) + order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id] + coordinator = Enterprise.find_by_id(params[:coordinator_id]) if params[:coordinator_id] + order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present? + enterprises = OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) return EnterpriseFee.for_enterprises(enterprises).order('enterprise_id', 'fee_type', 'name') else collection = EnterpriseFee.managed_by(spree_current_user).order('enterprise_id', 'fee_type', 'name') diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index e745d22909..1b8b010da1 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -95,10 +95,10 @@ module Admin def collection case action when :for_order_cycle - options = {} - options[:coordinator] = Enterprise.find(params[:coordinator_id]) if params[:coordinator_id] - options[:order_cycle] = OrderCycle.find(params[:order_cycle_id]) if params[:order_cycle_id] - return OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(options) + order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id] + coordinator = Enterprise.find_by_id(params[:coordinator_id]) if params[:coordinator_id] + order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present? + return OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) else # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now OpenFoodNetwork::Permissions.new(spree_current_user). diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 39727bbddb..7aa112816e 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -4,7 +4,7 @@ module OrderCyclesHelper end def permitted_enterprises_for(order_cycle) - OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle: order_cycle) + OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) end def permitted_producer_enterprises_for(order_cycle) diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 53976ee09a..3f2bad8ffa 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -23,7 +23,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # work out which variants should be editable within incoming exchanges from that enterprise editable = {} permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(order_cycle: object) + enterprises = permissions.order_cycle_enterprises_for(object) enterprises.each do |enterprise| variants = permissions.editable_variants_for_incoming_exchanges_between(enterprise, object.coordinator, order_cycle: object).pluck(:id) editable[enterprise.id] = variants if variants.any? @@ -36,7 +36,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # work out which variants should be editable within incoming exchanges from that enterprise editable = {} permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(order_cycle: object) + enterprises = permissions.order_cycle_enterprises_for(object) enterprises.each do |enterprise| variants = permissions.editable_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) editable[enterprise.id] = variants if variants.any? @@ -49,7 +49,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # work out which variants should be visible within outgoing exchanges from that enterprise visible = {} permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(order_cycle: object) + enterprises = permissions.order_cycle_enterprises_for(object) enterprises.each do |enterprise| variants = permissions.visible_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) visible[enterprise.id] = variants if variants.any? diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index b1d01c74bf..7cf8ea0f7a 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -8,7 +8,7 @@ - unless order_cycles_simple_index %td.suppliers - - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle: order_cycle)) + - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle)) - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 %span.with-tip{'data-powertip' => supplier_list} @@ -18,7 +18,7 @@ = supplier_list %td= order_cycle.coordinator.name %td.distributors - - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle: order_cycle)) + - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle)) - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 %span.with-tip{'data-powertip' => distributor_list} diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index a5ed5fefea..c23df65e8c 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -97,7 +97,7 @@ module OpenFoodNetwork def permitted_enterprises return @permitted_enterprises unless @permitted_enterprises.nil? @permitted_enterprises = OpenFoodNetwork::Permissions.new(@spree_current_user). - order_cycle_enterprises_for(order_cycle: @order_cycle) + order_cycle_enterprises_for(@order_cycle) end def user_manages_coordinator? diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index fcad0e829b..03e5d491e4 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -19,16 +19,9 @@ module OpenFoodNetwork # NOTE: the enterprises a given user can see actually in the OC interface depend on the relationships # of their enterprises to the coordinator of the order cycle, rather than on the order cycle itself # (until such time as we implement friends of friends) - def order_cycle_enterprises_for(options={}) - # Can provide a coordinator OR an order cycle. Use just coordinator for new order cycles - # if both are provided, coordinator will be ignored, and the coordinator of the OC will be used - return Enterprise.where("1=0") unless options[:coordinator] || options[:order_cycle] - coordinator = options[:coordinator] - order_cycle = nil - if options[:order_cycle] - order_cycle = options[:order_cycle] - coordinator = order_cycle.coordinator - end + def order_cycle_enterprises_for(order_cycle) + return Enterprise.where("1=0") unless order_cycle.andand.coordinator.present? + coordinator = order_cycle.coordinator if managed_enterprises.include? coordinator coordinator_permitted = [] diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 6cbe2c6f95..742fceb3d2 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -414,8 +414,9 @@ module Admin before do # As a user with permission controller.stub spree_current_user: user - Enterprise.stub find: "instance of Enterprise" - OrderCycle.stub find: "instance of OrderCycle" + OrderCycle.stub find_by_id: "existing OrderCycle" + Enterprise.stub find_by_id: "existing Enterprise" + OrderCycle.stub new: "new OrderCycle" OpenFoodNetwork::Permissions.stub(:new) { permission_mock } allow(permission_mock).to receive :order_cycle_enterprises_for @@ -424,28 +425,28 @@ module Admin context "when no order_cycle or coordinator is provided in params" do before { spree_get :for_order_cycle } it "returns an empty scope" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with({}) + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(nil) end end context "when an order_cycle_id is provided in params" do before { spree_get :for_order_cycle, order_cycle_id: 1 } - it "calls order_cycle_enterprises_for() with an :order_cycle option" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(order_cycle: "instance of OrderCycle") + it "calls order_cycle_enterprises_for() with the existing OrderCycle" do + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("existing OrderCycle") end end context "when a coordinator is provided in params" do before { spree_get :for_order_cycle, coordinator_id: 1 } - it "calls order_cycle_enterprises_for() with a :coordinator option" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(coordinator: "instance of Enterprise") + it "calls order_cycle_enterprises_for() with a new OrderCycle" do + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("new OrderCycle") end end context "when both an order cycle and a coordinator are provided in params" do before { spree_get :for_order_cycle, order_cycle_id: 1, coordinator_id: 1 } - it "calls order_cycle_enterprises_for() with both options" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(coordinator: "instance of Enterprise", order_cycle: "instance of OrderCycle") + it "calls order_cycle_enterprises_for() with the existing OrderCycle" do + expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("existing OrderCycle") end end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index adb319d801..31566cbe54 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -28,15 +28,19 @@ module OpenFoodNetwork let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } context "when no order_cycle or coordinator are provided for reference" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + it "returns an empty scope" do - expect(permissions.order_cycle_enterprises_for()).to be_empty + expect(permissions.order_cycle_enterprises_for(nil)).to be_empty end end context "as a manager of the coordinator" do it "returns the coordinator itself" do permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } - expect(permissions.order_cycle_enterprises_for(order_cycle: oc)).to include coordinator + expect(permissions.order_cycle_enterprises_for(oc)).to include coordinator end context "where P-OC has been granted to the coordinator by other enterprises" do @@ -47,7 +51,7 @@ module OpenFoodNetwork context "where the coordinator sells any" do it "returns enterprises which have granted P-OC to the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include hub expect(enterprises).to_not include producer end @@ -56,7 +60,7 @@ module OpenFoodNetwork context "where the coordinator sells 'own'" do before { coordinator.stub(:sells) { 'own' } } it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include hub, producer end end @@ -77,7 +81,7 @@ module OpenFoodNetwork let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include hub expect(enterprises).to_not include producer, coordinator end @@ -91,7 +95,7 @@ module OpenFoodNetwork let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } it "returns the producer" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include producer, hub end end @@ -100,7 +104,7 @@ module OpenFoodNetwork # No incoming exchange it "does not return the producer" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include producer end end @@ -115,7 +119,7 @@ module OpenFoodNetwork let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } it "returns the producer" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include producer, hub end end @@ -124,7 +128,7 @@ module OpenFoodNetwork # No incoming exchange it "does not return the producer" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include producer end end @@ -135,7 +139,7 @@ module OpenFoodNetwork # No outgoing exchange for my hub it "does not return my hub" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include hub, producer, coordinator end end @@ -143,7 +147,7 @@ module OpenFoodNetwork context "that has not granted P-OC to the coordinator" do it "does not return my hub" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include hub, producer, coordinator end @@ -151,7 +155,7 @@ module OpenFoodNetwork let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include hub expect(enterprises).to_not include producer, coordinator end @@ -161,7 +165,7 @@ module OpenFoodNetwork # TODO: update this when we are confident about P-OCs it "returns that producer as well" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end @@ -184,7 +188,7 @@ module OpenFoodNetwork let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } it "returns my producer" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include producer expect(enterprises).to_not include hub, coordinator end @@ -198,7 +202,7 @@ module OpenFoodNetwork let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns the hub as well" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end @@ -208,7 +212,7 @@ module OpenFoodNetwork # No outgoing exchange it "does not return the hub" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include hub end end @@ -223,7 +227,7 @@ module OpenFoodNetwork let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns the hub as well" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end @@ -233,7 +237,7 @@ module OpenFoodNetwork # No outgoing exchange it "does not return the hub" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include hub end end @@ -244,7 +248,7 @@ module OpenFoodNetwork # No incoming exchange for producer it "does not return my producer" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include hub, producer, coordinator end end @@ -252,7 +256,7 @@ module OpenFoodNetwork context "which has not granted P-OC to the coordinator" do it "does not return my producer" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to_not include producer end @@ -261,7 +265,7 @@ module OpenFoodNetwork # TODO: update this when we are confident about P-OCs it "returns my producer" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include producer expect(enterprises).to_not include hub, coordinator end @@ -272,7 +276,7 @@ module OpenFoodNetwork # TODO: update this when we are confident about P-OCs it "returns that hub as well" do - enterprises = permissions.order_cycle_enterprises_for(order_cycle: oc) + enterprises = permissions.order_cycle_enterprises_for(oc) expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end From b8ce6ed0fc2e706e05e09d230e26326722df34a2 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 13:28:49 +1000 Subject: [PATCH 321/384] Coordinator can see any enterprises that are already in the order cycle --- lib/open_food_network/permissions.rb | 17 ++++----- .../lib/open_food_network/permissions_spec.rb | 35 +++++++++++++++++-- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 03e5d491e4..9a98d7a750 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -24,20 +24,21 @@ module OpenFoodNetwork coordinator = order_cycle.coordinator if managed_enterprises.include? coordinator - coordinator_permitted = [] + coordinator_permitted = [coordinator] + all_active = [] - if coordinator.sells == "own" - # Coordinators that sell own can only see themselves in the OC interface - coordinator_permitted = [coordinator] - elsif coordinator.sells == "any" + if coordinator.sells == "any" # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) # If the coordinator sells any, relationships come into play - coordinator_permitted = granting(:add_to_order_cycle, to: [coordinator]).pluck(:id) - coordinator_permitted = coordinator_permitted | [coordinator] + granting(:add_to_order_cycle, to: [coordinator]).pluck(:id).each do |enterprise_id| + coordinator_permitted << enterprise_id + end + + all_active = order_cycle.suppliers.pluck(:id) | order_cycle.distributors.pluck(:id) end - Enterprise.where(id: coordinator_permitted) + Enterprise.where(id: coordinator_permitted | all_active) else # Any enterprises that I manage directly, which have granted P-OC to the coordinator managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises_in(order_cycle) ).pluck(:id) diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 31566cbe54..fb281300c3 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -38,15 +38,17 @@ module OpenFoodNetwork end context "as a manager of the coordinator" do - it "returns the coordinator itself" do + before do permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns the coordinator itself" do expect(permissions.order_cycle_enterprises_for(oc)).to include coordinator end context "where P-OC has been granted to the coordinator by other enterprises" do before do create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) - permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } end context "where the coordinator sells any" do @@ -65,6 +67,35 @@ module OpenFoodNetwork end end end + + context "where P-OC has not been granted to the coordinator by other enterprises" do + context "where the other enterprise are already in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + context "where the coordinator sells any" do + it "returns enterprises which have granted P-OC to the coordinator" do + enterprises = permissions.order_cycle_enterprises_for(oc) + expect(enterprises).to include hub, producer + end + end + + context "where the coordinator sells 'own'" do + before { coordinator.stub(:sells) { 'own' } } + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for(oc) + expect(enterprises).to_not include hub, producer + end + end + end + + context "where the other enterprises are not in the order cycle" do + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for(oc) + expect(enterprises).to_not include hub, producer + end + end + end end context "as a manager of a hub" do From b1624a733e59beff1608f395930c550b406c1456 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 13:38:37 +1000 Subject: [PATCH 322/384] Editable variants for incoming exchanges for hubs include those for producers that have granted my hub P-OC --- lib/open_food_network/permissions.rb | 8 +------- spec/lib/open_food_network/permissions_spec.rb | 12 +++++++++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 9a98d7a750..6a42dfcf93 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -152,13 +152,7 @@ module OpenFoodNetwork # Find the variants that a user can edit within incoming exchanges def editable_variants_for_incoming_exchanges_between(producer, coordinator, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # All variants belonging to the producer - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) - else - Spree::Variant.where("1=0") - end + visible_variants_for_incoming_exchanges_between(producer, coordinator, options) end # Find the variants that a user is permitted see within outgoing exchanges diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index fb281300c3..6da2c87215 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -759,8 +759,18 @@ module OpenFoodNetwork context "where the hub is in the order cycle" do let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } + it "returns variants produced by that producer only" do + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + it "does not return variants produced by that producer" do - visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) + visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) expect(visible).to_not include v1, v2 end end From 32cc17745aef1c4c07d1ed504fdda39bcbe44647 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 14:12:49 +1000 Subject: [PATCH 323/384] Non-coordinating user cannot alter important attributes of order cycle --- app/controllers/admin/order_cycles_controller.rb | 9 +++++++++ .../order_cycles/_name_and_timing_form.html.haml | 16 +++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index c8d5af73e6..b27a343ae3 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -7,6 +7,7 @@ module Admin before_filter :load_data_for_index, :only => :index before_filter :require_coordinator, only: :new + before_filter :remove_protected_attrs, only: [:update] around_filter :protect_invalid_destroy, only: :destroy @@ -120,5 +121,13 @@ module Admin flash[:error] = "That order cycle has been selected by a customer and cannot be deleted. To prevent customers from accessing it, please close it instead." end end + + def remove_protected_attrs + params[:order_cycle].delete :coordinator_id + + unless spree_current_user.admin? || Enterprise.managed_by(spree_current_user).include?(@order_cycle.coordinator) + params[:order_cycle].delete_if{ |k,v| [:name, :orders_open_at, :orders_close_at].include? k.to_sym } + end + end end end diff --git a/app/views/admin/order_cycles/_name_and_timing_form.html.haml b/app/views/admin/order_cycles/_name_and_timing_form.html.haml index ed24d20f39..be11d45372 100644 --- a/app/views/admin/order_cycles/_name_and_timing_form.html.haml +++ b/app/views/admin/order_cycles/_name_and_timing_form.html.haml @@ -1,15 +1,25 @@ +- as_coordinator = Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator .row .alpha.two.columns = f.label :name .fourteen.columns.omega - = f.text_field :name, 'ng-model' => 'order_cycle.name', 'required' => true + - if as_coordinator + = f.text_field :name, 'ng-model' => 'order_cycle.name', 'required' => true + - else + {{ order_cycle.name }} .row .alpha.two.columns = f.label :orders_open_at, 'Orders open' .six.columns - = f.text_field :orders_open_at, 'datetimepicker' => 'order_cycle.orders_open_at', 'ng-model' => 'order_cycle.orders_open_at' + - if as_coordinator + = f.text_field :orders_open_at, 'datetimepicker' => 'order_cycle.orders_open_at', 'ng-model' => 'order_cycle.orders_open_at' + - else + {{ order_cycle.orders_open_at }} .two.columns = f.label :orders_close_at, 'Orders close' .six.columns.omega - = f.text_field :orders_close_at, 'datetimepicker' => 'order_cycle.orders_close_at', 'ng-model' => 'order_cycle.orders_close_at' + - if as_coordinator + = f.text_field :orders_close_at, 'datetimepicker' => 'order_cycle.orders_close_at', 'ng-model' => 'order_cycle.orders_close_at' + - else + {{ order_cycle.orders_close_at }} From f6e635466bbcd7c228ed7b455a56d58de0a3cbac Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 14:31:18 +1000 Subject: [PATCH 324/384] Rearranging elements on the order cycle form a little bit --- app/views/admin/order_cycles/_form.html.haml | 11 ++--------- .../order_cycles/_name_and_timing_form.html.haml | 14 +++++++++----- spec/features/admin/order_cycles_spec.rb | 13 ++++--------- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 6d24e149f8..a5f7ec1518 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -1,5 +1,7 @@ = render 'name_and_timing_form', f: f +-if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator + = render 'coordinator_fees', f: f %h2 Incoming %table.exchanges @@ -20,15 +22,6 @@ = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' - -%h2 Coordinator --if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator - = f.label :coordinator_id, 'Coordinator' - = f.select :coordinator_id, permitted_coordinating_enterprise_options_for(@order_cycle), { include_blank: true }, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} - = render 'coordinator_fees', f: f -- else - = @order_cycle.coordinator.name - %h2 Outgoing %table.exchanges %thead diff --git a/app/views/admin/order_cycles/_name_and_timing_form.html.haml b/app/views/admin/order_cycles/_name_and_timing_form.html.haml index be11d45372..1bb60e784e 100644 --- a/app/views/admin/order_cycles/_name_and_timing_form.html.haml +++ b/app/views/admin/order_cycles/_name_and_timing_form.html.haml @@ -2,20 +2,24 @@ .row .alpha.two.columns = f.label :name - .fourteen.columns.omega + .six.columns.omega - if as_coordinator = f.text_field :name, 'ng-model' => 'order_cycle.name', 'required' => true - else {{ order_cycle.name }} - -.row - .alpha.two.columns + .two.columns = f.label :orders_open_at, 'Orders open' - .six.columns + .omega.six.columns - if as_coordinator = f.text_field :orders_open_at, 'datetimepicker' => 'order_cycle.orders_open_at', 'ng-model' => 'order_cycle.orders_open_at' - else {{ order_cycle.orders_open_at }} + +.row + .alpha.two.columns + = f.label :coordinator + .six.columns.omega + = @order_cycle.coordinator.name .two.columns = f.label :orders_close_at, 'Orders close' .six.columns.omega diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 2d6a854d5c..cb96799fbb 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -90,7 +90,6 @@ feature %q{ fill_in 'order_cycle_name', with: 'Plums & Avos' fill_in 'order_cycle_orders_open_at', with: '2040-11-06 06:00:00' fill_in 'order_cycle_orders_close_at', with: '2040-11-13 17:00:00' - select 'My coordinator', from: 'order_cycle_coordinator_id' # And I add a coordinator fee click_button 'Add coordinator fee' @@ -170,8 +169,7 @@ feature %q{ page.find('#order_cycle_name').value.should == oc.name page.find('#order_cycle_orders_open_at').value.should == oc.orders_open_at.to_s page.find('#order_cycle_orders_close_at').value.should == oc.orders_close_at.to_s - page.find('#order_cycle_coordinator_id').value.to_i.should == oc.coordinator_id - page.should have_selector "select[name='order_cycle_coordinator_fee_0_id']" + page.should have_content "COORDINATOR #{oc.coordinator.name}" # And I should see the suppliers page.should have_selector 'td.supplier_name', :text => oc.suppliers.first.name @@ -524,13 +522,11 @@ feature %q{ page.should have_content oc_user_coordinating.name page.should_not have_content oc_for_other_user.name - # The order cycle should show enterprises that I manage + # The order cycle should show all enterprises in the order cycle page.should have_selector 'td.suppliers', text: supplier_managed.name page.should have_selector 'td.distributors', text: distributor_managed.name - - # The order cycle should not show enterprises that I don't manage - page.should_not have_selector 'td.suppliers', text: supplier_unmanaged.name - page.should_not have_selector 'td.distributors', text: distributor_unmanaged.name + page.should have_selector 'td.suppliers', text: supplier_unmanaged.name + page.should have_selector 'td.distributors', text: distributor_unmanaged.name end scenario "creating a new order cycle" do @@ -551,7 +547,6 @@ feature %q{ select_incoming_variant supplier_managed, 0, product_managed.master select_incoming_variant supplier_permitted, 1, product_permitted.master - select 'Managed distributor', from: 'order_cycle_coordinator_id' click_button 'Add coordinator fee' select 'Managed distributor fee', from: 'order_cycle_coordinator_fee_0_id' From 61a9e2f7cc8518ca9e8c3cf4fcca39f2362c3316 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 8 Apr 2015 14:36:56 +1000 Subject: [PATCH 325/384] Add a referer for all admin enterprise controller specs --- spec/controllers/admin/enterprises_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 6cbe2c6f95..8599851310 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -14,6 +14,7 @@ module Admin let(:distributor) { create(:distributor_enterprise, owner: distributor_owner ) } let(:supplier) { create(:supplier_enterprise, owner: supplier_owner) } + before { @request.env['HTTP_REFERER'] = 'http://test.com/' } describe "creating an enterprise" do let(:country) { Spree::Country.find_by_name 'Australia' } @@ -142,7 +143,6 @@ module Admin let!(:property) { create(:property, name: "A nice name") } before do - @request.env['HTTP_REFERER'] = 'http://test.com/' login_as_enterprise_user [producer] end From fbcfe9b2a41b446580181b7e05cb6f94061a54b7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 10:31:17 +1000 Subject: [PATCH 326/384] Add viewing_as_coordiantor flag to serialized order cycles and a managed flag to serialized enterprises for_order_cycle(s) --- app/serializers/api/admin/order_cycle_serializer.rb | 5 +++++ app/views/admin/enterprises/for_order_cycle.rabl | 2 ++ app/views/admin/order_cycles/_exchange_form.html.haml | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 3f2bad8ffa..8d1ce4a086 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -1,5 +1,6 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges + attributes :viewing_as_coordinator attributes :editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges attributes :visible_variants_for_outgoing_exchanges @@ -13,6 +14,10 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer object.orders_close_at.to_s end + def viewing_as_coordinator + Enterprise.managed_by(options[:current_user]).include? object.coordinator + end + def exchanges scoped_exchanges = OpenFoodNetwork::Permissions.new(options[:current_user]).order_cycle_exchanges(object).order('id ASC') ActiveModel::ArraySerializer.new(scoped_exchanges, {each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user] }) diff --git a/app/views/admin/enterprises/for_order_cycle.rabl b/app/views/admin/enterprises/for_order_cycle.rabl index 1342d6eb1a..43cf016e48 100644 --- a/app/views/admin/enterprises/for_order_cycle.rabl +++ b/app/views/admin/enterprises/for_order_cycle.rabl @@ -2,6 +2,8 @@ collection @collection attributes :id, :name +node(:managed) { |enterprise| Enterprise.managed_by(spree_current_user).include? enterprise } + node(:supplied_products) do |enterprise| enterprise.supplied_products.not_deleted.map do |product| partial 'admin/enterprises/supplied_product', object: product diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index d37722a910..93db1ad4e9 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -9,9 +9,9 @@ selected - if type == 'distributor' %td.collection-details - = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', 'placeholder' => 'Ready for (ie. Date / Time)', 'ng-model' => 'exchange.pickup_time' + = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', 'placeholder' => 'Ready for (ie. Date / Time)', 'ng-model' => 'exchange.pickup_time', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator' %br/ - = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', 'placeholder' => 'Pick-up instructions', 'ng-model' => 'exchange.pickup_instructions' + = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', 'placeholder' => 'Pick-up instructions', 'ng-model' => 'exchange.pickup_instructions', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator' %td.fees %ol %li{'ng-repeat' => 'enterprise_fee in exchange.enterprise_fees'} From 133f7e4ec769eeb9a9e32b13e28c2b0b0aa01825 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 11:10:56 +1000 Subject: [PATCH 327/384] Add viewing_as_coodinator_of? helper method --- app/helpers/order_cycles_helper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 7aa112816e..aa7b52d19a 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -79,6 +79,10 @@ module OrderCyclesHelper Spree::Order.where(order_cycle_id: order_cycle).none? end + def viewing_as_coordinator_of?(order_cycle) + Enterprise.managed_by(spree_current_user).include? order_cycle.coordinator + end + private def validated_enterprise_options(enterprises, options={}) From c7157ce7bdf04c8491a27fa7b42f4b366a0bcb9b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 11:11:06 +1000 Subject: [PATCH 328/384] Applying new helper to order cycle name and timing partial --- .../admin/order_cycles/_name_and_timing_form.html.haml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/admin/order_cycles/_name_and_timing_form.html.haml b/app/views/admin/order_cycles/_name_and_timing_form.html.haml index 1bb60e784e..502d12ca3a 100644 --- a/app/views/admin/order_cycles/_name_and_timing_form.html.haml +++ b/app/views/admin/order_cycles/_name_and_timing_form.html.haml @@ -1,16 +1,15 @@ -- as_coordinator = Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator .row .alpha.two.columns = f.label :name .six.columns.omega - - if as_coordinator + - if viewing_as_coordinator_of?(order_cycle) = f.text_field :name, 'ng-model' => 'order_cycle.name', 'required' => true - else {{ order_cycle.name }} .two.columns = f.label :orders_open_at, 'Orders open' .omega.six.columns - - if as_coordinator + - if viewing_as_coordinator_of?(order_cycle) = f.text_field :orders_open_at, 'datetimepicker' => 'order_cycle.orders_open_at', 'ng-model' => 'order_cycle.orders_open_at' - else {{ order_cycle.orders_open_at }} @@ -23,7 +22,7 @@ .two.columns = f.label :orders_close_at, 'Orders close' .six.columns.omega - - if as_coordinator + - if viewing_as_coordinator_of?(order_cycle) = f.text_field :orders_close_at, 'datetimepicker' => 'order_cycle.orders_close_at', 'ng-model' => 'order_cycle.orders_close_at' - else {{ order_cycle.orders_close_at }} From 8a3126f1171725c264411823bd7ab0635b57a481 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 11:11:46 +1000 Subject: [PATCH 329/384] Using helper to disable form elements on order cycle index page --- app/views/admin/order_cycles/_row.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 7cf8ea0f7a..f30c740f03 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -3,8 +3,8 @@ %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= order_cycle_form.text_field :orders_open_at, :class => "#{viewing_as_coordinator_of?(order_cycle) ? 'datetimepicker' : ''}", :value => order_cycle.orders_open_at, :disabled => !viewing_as_coordinator_of?(order_cycle) + %td= order_cycle_form.text_field :orders_close_at, :class => "#{viewing_as_coordinator_of?(order_cycle) ? 'datetimepicker' : ''}", :value => order_cycle.orders_close_at, :disabled => !viewing_as_coordinator_of?(order_cycle) - unless order_cycles_simple_index %td.suppliers From 60a3d8e0d1bd2e8a3d3c5d369fc8f9dcb4b20f0e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 12:27:43 +1000 Subject: [PATCH 330/384] Restrict bulk updating order cycles at controller level --- .../admin/order_cycles_controller.rb | 12 +++++- .../admin/order_cycles_controller_spec.rb | 38 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index b27a343ae3..6c9891baec 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -8,6 +8,7 @@ module Admin before_filter :load_data_for_index, :only => :index before_filter :require_coordinator, only: :new before_filter :remove_protected_attrs, only: [:update] + before_filter :remove_unauthorized_bulk_attrs, only: [:bulk_update] around_filter :protect_invalid_destroy, only: :destroy @@ -125,9 +126,18 @@ module Admin def remove_protected_attrs params[:order_cycle].delete :coordinator_id - unless spree_current_user.admin? || Enterprise.managed_by(spree_current_user).include?(@order_cycle.coordinator) + unless Enterprise.managed_by(spree_current_user).include?(@order_cycle.coordinator) params[:order_cycle].delete_if{ |k,v| [:name, :orders_open_at, :orders_close_at].include? k.to_sym } end end + + def remove_unauthorized_bulk_attrs + params[:order_cycle_set][:collection_attributes].each do |i, hash| + order_cycle = OrderCycle.find(hash[:id]) + unless Enterprise.managed_by(spree_current_user).include?(order_cycle.andand.coordinator) + params[:order_cycle_set][:collection_attributes].delete i + end + end + end end end diff --git a/spec/controllers/admin/order_cycles_controller_spec.rb b/spec/controllers/admin/order_cycles_controller_spec.rb index b58884086c..9e996f0da7 100644 --- a/spec/controllers/admin/order_cycles_controller_spec.rb +++ b/spec/controllers/admin/order_cycles_controller_spec.rb @@ -57,6 +57,44 @@ module Admin end end + describe "bulk_update" do + let(:oc) { create(:simple_order_cycle) } + let!(:coordinator) { oc.coordinator } + + context "when I manage the coordinator of an order cycle" do + before { create(:enterprise_role, user: distributor_owner, enterprise: coordinator) } + + it "updates order cycle properties" do + spree_put :bulk_update, order_cycle_set: { collection_attributes: { '0' => { + id: oc.id, + orders_open_at: Date.today - 21.days, + orders_close_at: Date.today + 21.days, + } } } + + oc.reload + expect(oc.orders_open_at.to_date).to eq Date.today - 21.days + expect(oc.orders_close_at.to_date).to eq Date.today + 21.days + end + end + + context "when I do not manage the coordinator of an order cycle" do + # I need to manage a hub in order to access the bulk_update action + let!(:another_distributor) { create(:distributor_enterprise, users: [distributor_owner]) } + + it "doesn't update order cycle properties" do + spree_put :bulk_update, order_cycle_set: { collection_attributes: { '0' => { + id: oc.id, + orders_open_at: Date.today - 21.days, + orders_close_at: Date.today + 21.days, + } } } + + oc.reload + expect(oc.orders_open_at.to_date).to_not eq Date.today - 21.days + expect(oc.orders_close_at.to_date).to_not eq Date.today + 21.days + end + end + end + describe "destroy" do let!(:distributor) { create(:distributor_enterprise, owner: distributor_owner) } From 430320e3f9f15904d04a1f1163e4709a7167fdfe Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 13:28:41 +1000 Subject: [PATCH 331/384] Oopsie! --- .../admin/order_cycles/_name_and_timing_form.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/admin/order_cycles/_name_and_timing_form.html.haml b/app/views/admin/order_cycles/_name_and_timing_form.html.haml index 502d12ca3a..703d8094b9 100644 --- a/app/views/admin/order_cycles/_name_and_timing_form.html.haml +++ b/app/views/admin/order_cycles/_name_and_timing_form.html.haml @@ -2,14 +2,14 @@ .alpha.two.columns = f.label :name .six.columns.omega - - if viewing_as_coordinator_of?(order_cycle) + - if viewing_as_coordinator_of?(@order_cycle) = f.text_field :name, 'ng-model' => 'order_cycle.name', 'required' => true - else {{ order_cycle.name }} .two.columns = f.label :orders_open_at, 'Orders open' .omega.six.columns - - if viewing_as_coordinator_of?(order_cycle) + - if viewing_as_coordinator_of?(@order_cycle) = f.text_field :orders_open_at, 'datetimepicker' => 'order_cycle.orders_open_at', 'ng-model' => 'order_cycle.orders_open_at' - else {{ order_cycle.orders_open_at }} @@ -22,7 +22,7 @@ .two.columns = f.label :orders_close_at, 'Orders close' .six.columns.omega - - if viewing_as_coordinator_of?(order_cycle) + - if viewing_as_coordinator_of?(@order_cycle) = f.text_field :orders_close_at, 'datetimepicker' => 'order_cycle.orders_close_at', 'ng-model' => 'order_cycle.orders_close_at' - else {{ order_cycle.orders_close_at }} From 69004ac47737274c38cc0bff56427f157416ea81 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 13:29:25 +1000 Subject: [PATCH 332/384] Select all box only selects variants I have permission to edit --- .../admin/order_cycle.js.erb.coffee | 24 ++++++++++++++----- .../filter_exchange_variants.js.coffee | 6 +++++ .../visible_exchange_variants.js.coffee | 3 --- ...change_distributed_products_form.html.haml | 2 +- .../order_cycles/_exchange_form.html.haml | 2 +- ..._exchange_supplied_products_form.html.haml | 2 +- 6 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 app/assets/javascripts/admin/order_cycles/filters/filter_exchange_variants.js.coffee delete mode 100644 app/assets/javascripts/admin/order_cycles/filters/visible_exchange_variants.js.coffee diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 64f06e43a8..924b58f389 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -1,5 +1,5 @@ angular.module('admin.order_cycles', ['ngResource']) - .controller('AdminCreateOrderCycleCtrl', ['$scope', 'OrderCycle', 'Enterprise', 'EnterpriseFee', 'ocInstance', ($scope, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> + .controller('AdminCreateOrderCycleCtrl', ['$scope', '$filter', 'OrderCycle', 'Enterprise', 'EnterpriseFee', 'ocInstance', ($scope, $filter, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> $scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id) $scope.supplied_products = Enterprise.supplied_products $scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id) @@ -27,8 +27,14 @@ angular.module('admin.order_cycles', ['ngResource']) $scope.variantSuppliedToOrderCycle = (variant) -> OrderCycle.variantSuppliedToOrderCycle(variant) - $scope.incomingExchangesVariants = -> - OrderCycle.incomingExchangesVariants() + $scope.editableIncomingExchangeVariantsFor = (enterprise_id) -> + $filter('filterExchangeVariants')($scope.suppliedVariants(enterprise_id), $scope.order_cycle.editable_variants_for_incoming_exchanges[enterprise_id]) + + $scope.outgoingExchangeVariantsFor = (enterprise_id) -> + $filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id]) + + $scope.editableOutgoingExchangeVariantsFor = (enterprise_id) -> + $filter('filterExchangeVariants')($scope.outgoingExchangeVariantsFor(enterprise_id), $scope.order_cycle.editable_variants_for_outgoing_exchanges[enterprise_id]) $scope.exchangeDirection = (exchange) -> OrderCycle.exchangeDirection(exchange) @@ -79,7 +85,7 @@ angular.module('admin.order_cycles', ['ngResource']) OrderCycle.create() ]) - .controller('AdminEditOrderCycleCtrl', ['$scope', '$location', 'OrderCycle', 'Enterprise', 'EnterpriseFee', ($scope, $location, OrderCycle, Enterprise, EnterpriseFee) -> + .controller('AdminEditOrderCycleCtrl', ['$scope', '$filter', '$location', 'OrderCycle', 'Enterprise', 'EnterpriseFee', ($scope, $filter, $location, OrderCycle, Enterprise, EnterpriseFee) -> order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] $scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id) $scope.supplied_products = Enterprise.supplied_products @@ -108,8 +114,14 @@ angular.module('admin.order_cycles', ['ngResource']) $scope.variantSuppliedToOrderCycle = (variant) -> OrderCycle.variantSuppliedToOrderCycle(variant) - $scope.incomingExchangesVariants = -> - OrderCycle.incomingExchangesVariants() + $scope.editableIncomingExchangeVariantsFor = (enterprise_id) -> + $filter('filterExchangeVariants')($scope.suppliedVariants(enterprise_id), $scope.order_cycle.editable_variants_for_incoming_exchanges[enterprise_id]) + + $scope.outgoingExchangeVariantsFor = (enterprise_id) -> + $filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id]) + + $scope.editableOutgoingExchangeVariantsFor = (enterprise_id) -> + $filter('filterExchangeVariants')($scope.outgoingExchangeVariantsFor(enterprise_id), $scope.order_cycle.editable_variants_for_outgoing_exchanges[enterprise_id]) $scope.exchangeDirection = (exchange) -> OrderCycle.exchangeDirection(exchange) diff --git a/app/assets/javascripts/admin/order_cycles/filters/filter_exchange_variants.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/filter_exchange_variants.js.coffee new file mode 100644 index 0000000000..2b22b1d60c --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/filters/filter_exchange_variants.js.coffee @@ -0,0 +1,6 @@ +angular.module("admin.order_cycles").filter "filterExchangeVariants", -> + return (variants, rules) -> + if variants? && rules? + return (variant for variant in variants when variant in rules) + else + return [] diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_exchange_variants.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_exchange_variants.js.coffee deleted file mode 100644 index b898df5371..0000000000 --- a/app/assets/javascripts/admin/order_cycles/filters/visible_exchange_variants.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -angular.module("admin.order_cycles").filter "visibleExchangeVariants", -> - return (variants, exchange, rules) -> - return (variant for variant in variants when variant in rules[exchange.enterprise_id]) diff --git a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml index 5197242d8a..575e28bf04 100644 --- a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml @@ -1,7 +1,7 @@ %td{:colspan => 3} .exchange-select-all-variants %label - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, incomingExchangesVariants(), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, editableOutgoingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' Select all -# Scope product list based on permissions the current user has to view variants in this exchange diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index 93db1ad4e9..06920bf254 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -5,7 +5,7 @@ - if type == 'supplier' {{ enterpriseTotalVariants(enterprises[exchange.enterprise_id]) }} - else - {{ (incomingExchangesVariants() | visibleExchangeVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges).length }} + {{ (outgoingExchangeVariantsFor(exchange.enterprise_id)).length }} selected - if type == 'distributor' %td.collection-details diff --git a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml index 869a862a1b..034dff192a 100644 --- a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml @@ -2,7 +2,7 @@ %td{:colspan => 3} .exchange-select-all-variants %label - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants' + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, editableIncomingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants' Select all -# No need to scope product list based on permissions, because if an incoming exchange is visible, From c28ebf63ab932bb79f5e4ac9669cb6fffc9a795c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 13:41:43 +1000 Subject: [PATCH 333/384] As participating Hub, I cannot edit incoming exchanges despite being granted P-OC by the producer --- lib/open_food_network/permissions.rb | 8 +++++++- .../lib/open_food_network/permissions_spec.rb | 20 +++---------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 6a42dfcf93..9a98d7a750 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -152,7 +152,13 @@ module OpenFoodNetwork # Find the variants that a user can edit within incoming exchanges def editable_variants_for_incoming_exchanges_between(producer, coordinator, options={}) - visible_variants_for_incoming_exchanges_between(producer, coordinator, options) + return Spree::Variant.where("1=0") unless options[:order_cycle] + if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) + # All variants belonging to the producer + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + Spree::Variant.where("1=0") + end end # Find the variants that a user is permitted see within outgoing exchanges diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 6da2c87215..ddbdd5a359 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -756,23 +756,9 @@ module OpenFoodNetwork create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) end - context "where the hub is in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - it "returns variants produced by that producer only" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "where the hub is not in the order cycle" do - # No outgoing exchange - - it "does not return variants produced by that producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to_not include v1, v2 - end + it "does not return variants produced by that producer" do + visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) + expect(visible).to_not include v1, v2 end end end From 81a745420eb416a0d414b6ab826aeb3023a38553 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 15:24:45 +1000 Subject: [PATCH 334/384] Only check editable status of variants when actually selecting --- .../admin/order_cycle.js.erb.coffee | 16 ++------------ .../services/order_cycle.js.coffee | 5 ++++- .../api/admin/order_cycle_serializer.rb | 2 +- ...change_distributed_products_form.html.haml | 2 +- .../order_cycles/_exchange_form.html.haml | 2 +- ..._exchange_supplied_products_form.html.haml | 2 +- .../unit/order_cycle_spec.js.coffee | 21 +++++++++++++++---- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 924b58f389..2b5ca05f42 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -27,15 +27,9 @@ angular.module('admin.order_cycles', ['ngResource']) $scope.variantSuppliedToOrderCycle = (variant) -> OrderCycle.variantSuppliedToOrderCycle(variant) - $scope.editableIncomingExchangeVariantsFor = (enterprise_id) -> - $filter('filterExchangeVariants')($scope.suppliedVariants(enterprise_id), $scope.order_cycle.editable_variants_for_incoming_exchanges[enterprise_id]) - - $scope.outgoingExchangeVariantsFor = (enterprise_id) -> + $scope.incomingExchangeVariantsFor = (enterprise_id) -> $filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id]) - $scope.editableOutgoingExchangeVariantsFor = (enterprise_id) -> - $filter('filterExchangeVariants')($scope.outgoingExchangeVariantsFor(enterprise_id), $scope.order_cycle.editable_variants_for_outgoing_exchanges[enterprise_id]) - $scope.exchangeDirection = (exchange) -> OrderCycle.exchangeDirection(exchange) @@ -114,15 +108,9 @@ angular.module('admin.order_cycles', ['ngResource']) $scope.variantSuppliedToOrderCycle = (variant) -> OrderCycle.variantSuppliedToOrderCycle(variant) - $scope.editableIncomingExchangeVariantsFor = (enterprise_id) -> - $filter('filterExchangeVariants')($scope.suppliedVariants(enterprise_id), $scope.order_cycle.editable_variants_for_incoming_exchanges[enterprise_id]) - - $scope.outgoingExchangeVariantsFor = (enterprise_id) -> + $scope.incomingExchangeVariantsFor = (enterprise_id) -> $filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id]) - $scope.editableOutgoingExchangeVariantsFor = (enterprise_id) -> - $filter('filterExchangeVariants')($scope.outgoingExchangeVariantsFor(enterprise_id), $scope.order_cycle.editable_variants_for_outgoing_exchanges[enterprise_id]) - $scope.exchangeDirection = (exchange) -> OrderCycle.exchangeDirection(exchange) diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee index 98b685766e..95dc62ca4e 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee @@ -22,7 +22,9 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) exchange.showProducts = !exchange.showProducts setExchangeVariants: (exchange, variants, selected) -> - exchange.variants[variant] = selected for variant in variants + direction = if exchange.incoming then "incoming" else "outgoing" + editable = @order_cycle["editable_variants_for_#{direction}_exchanges"][exchange.enterprise_id] || [] + exchange.variants[variant] = selected for variant in variants when variant in editable addSupplier: (new_supplier_id) -> this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []}) @@ -162,6 +164,7 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) stripNonSubmittableAttributes: (order_cycle) -> delete order_cycle.id + delete order_cycle.viewing_as_coordinator delete order_cycle.editable_variants_for_incoming_exchanges delete order_cycle.editable_variants_for_outgoing_exchanges delete order_cycle.visible_variants_for_outgoing_exchanges diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 8d1ce4a086..d907d3ef6d 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -1,8 +1,8 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges - attributes :viewing_as_coordinator attributes :editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges attributes :visible_variants_for_outgoing_exchanges + attributes :viewing_as_coordinator has_many :coordinator_fees, serializer: Api::IdSerializer diff --git a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml index 575e28bf04..1616561a8d 100644 --- a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml @@ -1,7 +1,7 @@ %td{:colspan => 3} .exchange-select-all-variants %label - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, editableOutgoingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, incomingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' Select all -# Scope product list based on permissions the current user has to view variants in this exchange diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index 06920bf254..d26691cc30 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -5,7 +5,7 @@ - if type == 'supplier' {{ enterpriseTotalVariants(enterprises[exchange.enterprise_id]) }} - else - {{ (outgoingExchangeVariantsFor(exchange.enterprise_id)).length }} + {{ (incomingExchangeVariantsFor(exchange.enterprise_id)).length }} selected - if type == 'distributor' %td.collection-details diff --git a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml index 034dff192a..869a862a1b 100644 --- a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml @@ -2,7 +2,7 @@ %td{:colspan => 3} .exchange-select-all-variants %label - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, editableIncomingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants' + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants' Select all -# No need to scope product list based on permissions, because if an incoming exchange is visible, diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index bee45ae028..c14de6742d 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -502,10 +502,23 @@ describe 'OrderCycle services', -> expect(exchange.showProducts).toEqual(true) describe "setting exchange variants", -> - it "sets all variants to the provided value", -> - exchange = {variants: {2: false}} - OrderCycle.setExchangeVariants(exchange, [1, 2, 3], true) - expect(exchange.variants).toEqual {1: true, 2: true, 3: true} + describe "when I have permissions to edit the variants", -> + beforeEach -> + OrderCycle.order_cycle["editable_variants_for_outgoing_exchanges"] = { 1: [1, 2, 3] } + + it "sets all variants to the provided value", -> + exchange = { enterprise_id: 1, incoming: false, variants: {2: false}} + OrderCycle.setExchangeVariants(exchange, [1, 2, 3], true) + expect(exchange.variants).toEqual {1: true, 2: true, 3: true} + + describe "when I don't have permissions to edit the variants", -> + beforeEach -> + OrderCycle.order_cycle["editable_variants_for_outgoing_exchanges"] = { 1: [] } + + it "does not change variants to the provided value", -> + exchange = { enterprise_id: 1, incoming: false, variants: {2: false}} + OrderCycle.setExchangeVariants(exchange, [1, 2, 3], true) + expect(exchange.variants).toEqual {2: false} describe 'adding suppliers', -> exchange = null From e26ebf423946cd39241614852d8d60f3f8f040a3 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 9 Apr 2015 17:15:57 +1000 Subject: [PATCH 335/384] Serialize only activated hubs and producers of an enterprise --- app/serializers/api/enterprise_serializer.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 95ef4cec60..532887ae01 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -36,12 +36,10 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer :long_description, :website, :instagram, :linkedin, :twitter, :facebook, :is_primary_producer, :is_distributor, :phone, :visible, :email, :hash, :logo, :promo_image, :path, :pickup, :delivery, - :icon, :icon_font, :producer_icon_font, :category + :icon, :icon_font, :producer_icon_font, :category, :producers, :hubs has_many :distributed_taxons, key: :taxons, serializer: Api::IdSerializer has_many :supplied_taxons, serializer: Api::IdSerializer - has_many :distributors, key: :hubs, serializer: Api::IdSerializer - has_many :suppliers, key: :producers, serializer: Api::IdSerializer has_one :address, serializer: Api::AddressSerializer @@ -73,6 +71,14 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer enterprise_shop_path(object) end + def producers + ActiveModel::ArraySerializer.new(object.suppliers.activated, {each_serializer: Api::IdSerializer}) + end + + def hubs + ActiveModel::ArraySerializer.new(object.distributors.activated, {each_serializer: Api::IdSerializer}) + end + # Map svg icons. def icon icons = { From 6f24e969cebcd4f5164eb485852f077a65050eed Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 9 Apr 2015 17:20:31 +1000 Subject: [PATCH 336/384] Removing stupid unusable spec --- .../enterprises/for_order_cycle.rabl_spec.rb | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 spec/views/admin/enterprises/for_order_cycle.rabl_spec.rb diff --git a/spec/views/admin/enterprises/for_order_cycle.rabl_spec.rb b/spec/views/admin/enterprises/for_order_cycle.rabl_spec.rb deleted file mode 100644 index 1585d08e43..0000000000 --- a/spec/views/admin/enterprises/for_order_cycle.rabl_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe "admin/enterprises/for_order_cycle.rabl" do - let(:enterprise) { create(:distributor_enterprise) } - let!(:product) { create(:simple_product, supplier: enterprise) } - let!(:deleted_product) { create(:simple_product, supplier: enterprise, deleted_at: 1.day.ago) } - let(:render) { Rabl.render([enterprise], 'admin/enterprises/for_order_cycle', view_path: 'app/views', scope: RablHelper::FakeContext.instance) } - - describe "supplied products" do - it "does not render deleted products" do - render.should have_json_size(1).at_path '0/supplied_products' - render.should be_json_eql(product.master.id).at_path '0/supplied_products/0/master_id' - end - end -end From 2280a71b236bf3e86518110244958e78a2046aae Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 10 Apr 2015 09:25:40 +1000 Subject: [PATCH 337/384] Replace for_order_cycle rabl template with a serializer --- .../admin/enterprises_controller.rb | 11 ++++++++++ .../for_order_cycle/enterprise_serializer.rb | 13 ++++++++++++ .../supplied_product_serializer.rb | 21 +++++++++++++++++++ .../admin/enterprises/_supplied_product.rabl | 10 --------- .../admin/enterprises/for_order_cycle.rabl | 11 ---------- .../enterprise_serializer_spec.rb | 13 ++++++++++++ 6 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb create mode 100644 app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb delete mode 100644 app/views/admin/enterprises/_supplied_product.rabl delete mode 100644 app/views/admin/enterprises/for_order_cycle.rabl create mode 100644 spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 1b6567c616..de60a43edb 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -66,6 +66,17 @@ module Admin end end + def for_order_cycle + respond_to do |format| + format.html + format.json do + render json: ActiveModel::ArraySerializer.new( @collection, + each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, spree_current_user: spree_current_user + ).to_json + end + end + end + protected def build_resource_with_address diff --git a/app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb b/app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb new file mode 100644 index 0000000000..46afca46c5 --- /dev/null +++ b/app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb @@ -0,0 +1,13 @@ +class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer + attributes :id, :name, :managed, :supplied_products + + def managed + Enterprise.managed_by(options[:spree_current_user]).include? object + end + + def supplied_products + objects = object.supplied_products.not_deleted + serializer = Api::Admin::ForOrderCycle::SuppliedProductSerializer + ActiveModel::ArraySerializer.new(objects, each_serializer: serializer ) + end +end diff --git a/app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb b/app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb new file mode 100644 index 0000000000..dbe3ec7a48 --- /dev/null +++ b/app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb @@ -0,0 +1,21 @@ +class Api::Admin::ForOrderCycle::SuppliedProductSerializer < ActiveModel::Serializer + attributes :name, :supplier_name, :image_url, :master_id, :variants + + def supplier_name + object.supplier.andand.name + end + + def image_url + object.images.present? ? object.images.first.attachment.url(:mini) : nil + end + + def master_id + object.master.id + end + + def variants + object.variants.map do |variant| + { id: variant.id, label: variant.full_name } + end + end +end diff --git a/app/views/admin/enterprises/_supplied_product.rabl b/app/views/admin/enterprises/_supplied_product.rabl deleted file mode 100644 index 14835f2dbe..0000000000 --- a/app/views/admin/enterprises/_supplied_product.rabl +++ /dev/null @@ -1,10 +0,0 @@ -object @product - -attributes :name -node(:supplier_name) { |p| p.supplier.andand.name } -node(:image_url) { |p| p.images.present? ? p.images.first.attachment.url(:mini) : nil } -node(:master_id) { |p| p.master.id } -child variants: :variants do |variant| - attributes :id - node(:label) { |v| v.full_name } -end diff --git a/app/views/admin/enterprises/for_order_cycle.rabl b/app/views/admin/enterprises/for_order_cycle.rabl deleted file mode 100644 index 43cf016e48..0000000000 --- a/app/views/admin/enterprises/for_order_cycle.rabl +++ /dev/null @@ -1,11 +0,0 @@ -collection @collection - -attributes :id, :name - -node(:managed) { |enterprise| Enterprise.managed_by(spree_current_user).include? enterprise } - -node(:supplied_products) do |enterprise| - enterprise.supplied_products.not_deleted.map do |product| - partial 'admin/enterprises/supplied_product', object: product - end -end diff --git a/spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb b/spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb new file mode 100644 index 0000000000..3d49c1a2ca --- /dev/null +++ b/spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb @@ -0,0 +1,13 @@ +describe Api::Admin::ForOrderCycle::EnterpriseSerializer do + let(:enterprise) { create(:distributor_enterprise) } + let!(:product) { create(:simple_product, supplier: enterprise) } + let!(:deleted_product) { create(:simple_product, supplier: enterprise, deleted_at: Time.now) } + let(:serialized_enterprise) { Api::Admin::ForOrderCycle::EnterpriseSerializer.new(enterprise, spree_current_user: enterprise.owner ).to_json } + + describe "supplied products" do + it "does not render deleted products" do + expect(serialized_enterprise).to have_json_size(1).at_path 'supplied_products' + expect(serialized_enterprise).to be_json_eql(product.master.id).at_path 'supplied_products/0/master_id' + end + end +end From 226e2f0e2c5ebca6f560ff0dd76879f8a2734b11 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 10 Apr 2015 09:26:20 +1000 Subject: [PATCH 338/384] Fixing error caused by order show template doing a lookup based on current distributor rather than order distributor --- app/views/spree/orders/show.html.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/spree/orders/show.html.haml b/app/views/spree/orders/show.html.haml index 0f8174240f..f9d2830486 100644 --- a/app/views/spree/orders/show.html.haml +++ b/app/views/spree/orders/show.html.haml @@ -1,23 +1,23 @@ -= inject_enterprises += inject_enterprises .darkswarm - content_for :order_cycle_form do %strong.avenir Order ready on - if @order.order_cycle - = pickup_time @order.order_cycle + = @order.order_cycle.pickup_time_for(@order.distributor) - else = @order.distributor.next_collection_at = render partial: "shopping_shared/details" - %fieldset#order_summary{"data-hook" => ""} + %fieldset#order_summary{"data-hook" => ""} .row .columns.large-12.text-center %h2 Order confirmation = " #" + @order.number - + #order{"data-hook" => ""} - if params.has_key? :checkout_complete %h1= t(:thank_you_for_your_order) From 96c173414b28f5336da114192a1e57233abc57a1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 10 Apr 2015 12:48:27 +1000 Subject: [PATCH 339/384] Adding has_permission method to enterprise_relationship --- app/models/enterprise_relationship.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index 600ef87f11..fbdef9d52c 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -28,4 +28,8 @@ class EnterpriseRelationship < ActiveRecord::Base def permissions_list=(perms) perms.andand.each { |name| permissions.build name: name } end + + def has_permission?(name) + permissions.map(&:name).map(&:to_sym).include? name.to_sym + end end From 1e6fd94663ab84422066fb5a97c3b036add424ce Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 10 Apr 2015 12:51:15 +1000 Subject: [PATCH 340/384] Adding rake task to create E2E relationships with P-OC based on past order_cycles --- lib/tasks/data.rake | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 lib/tasks/data.rake diff --git a/lib/tasks/data.rake b/lib/tasks/data.rake new file mode 100644 index 0000000000..45985e6c50 --- /dev/null +++ b/lib/tasks/data.rake @@ -0,0 +1,56 @@ +namespace :openfoodnetwork do + namespace :data do + desc "Adding relationships based on recent order cycles" + task :create_order_cycle_relationships => :environment do + # For each order cycle which was modified within the past 3 months + OrderCycle.where('updated_at > ?', Date.today - 3.months).each do |order_cycle| + # Cycle through the incoming exchanges + order_cycle.exchanges.incoming.each do |exchange| + # Ensure that an enterprise relationship from the producer to the coordinator exists + relationship = EnterpriseRelationship.where(parent_id: exchange.sender_id, child_id: exchange.receiver_id).first + unless relationship.present? + puts "CREATING: #{exchange.sender.name} TO #{exchange.receiver.name}" + relationship = EnterpriseRelationship.create!(parent_id: exchange.sender_id, child_id: exchange.receiver_id) + end + # And that P-OC is granted + unless relationship.has_permission?(:add_to_order_cycle) + puts "PERMITTING: #{exchange.sender.name} TO #{exchange.receiver.name}" + relationship.permissions.create!(name: :add_to_order_cycle) + end + end + + # Cycle through the outgoing exchanges + order_cycle.exchanges.outgoing.each do |exchange| + # Enure that an enterprise relationship from the hub to the coordinator exists + relationship = EnterpriseRelationship.where(parent_id: exchange.receiver_id, child_id: exchange.sender_id).first + unless relationship.present? + puts "CREATING: #{exchange.receiver.name} TO #{exchange.sender.name}" + relationship = EnterpriseRelationship.create!(parent_id: exchange.receiver_id, child_id: exchange.sender_id) + end + # And that P-OC is granted + unless relationship.has_permission?(:add_to_order_cycle) + puts "PERMITTING: #{exchange.receiver.name} TO #{exchange.sender.name}" + relationship.permissions.create!(name: :add_to_order_cycle) + end + + # For each variant in the exchange + products = Spree::Product.joins(:variants_including_master).where('spree_variants.id IN (?)', exchange.variants).pluck(:id).uniq + producers = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).uniq + producers.each do |producer| + # Ensure that an enterprise relationship from the producer to the hub exists + relationship = EnterpriseRelationship.where(parent_id: producer.id, child_id: exchange.receiver_id).first + unless relationship.present? + puts "CREATING: #{producer.name} TO #{exchange.receiver.name}" + relationship = EnterpriseRelationship.create!(parent_id: producer.id, child_id: exchange.receiver_id) + end + # And that P-OC is granted + unless relationship.has_permission?(:add_to_order_cycle) + puts "PERMITTING: #{producer.name} TO #{exchange.receiver.name}" + relationship.permissions.create!(name: :add_to_order_cycle) + end + end + end + end + end + end +end From 1e168afd711073ffe0c04d05aad3f616fbc98b5e Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 10 Apr 2015 12:55:20 +1000 Subject: [PATCH 341/384] giving the enterprise serializer the active_distributors --- app/views/groups/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index dfa780f688..0459b2fc52 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -22,7 +22,7 @@ %tab{heading: 'Map', active: "active(\'\')", select: "select(\'\')"} - = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer + = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer, active_distributors: @active_distributors .map-container %map{"ng-controller" => "MapCtrl", "ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} From 72403fd0219deb9b3afcf706e4b1d1874294e761 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 10 Apr 2015 13:16:21 +1000 Subject: [PATCH 342/384] Fixing broken enterprise controller spec --- app/controllers/admin/enterprises_controller.rb | 1 - spec/controllers/admin/enterprises_controller_spec.rb | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index de60a43edb..bb13bb6739 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -68,7 +68,6 @@ module Admin def for_order_cycle respond_to do |format| - format.html format.json do render json: ActiveModel::ArraySerializer.new( @collection, each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, spree_current_user: spree_current_user diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index c383101d25..53edeae6b7 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -419,32 +419,33 @@ module Admin OrderCycle.stub new: "new OrderCycle" OpenFoodNetwork::Permissions.stub(:new) { permission_mock } - allow(permission_mock).to receive :order_cycle_enterprises_for + allow(permission_mock).to receive(:order_cycle_enterprises_for) + allow(ActiveModel::ArraySerializer).to receive(:new) { "" } end context "when no order_cycle or coordinator is provided in params" do - before { spree_get :for_order_cycle } + before { spree_get :for_order_cycle, format: :json } it "returns an empty scope" do expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(nil) end end context "when an order_cycle_id is provided in params" do - before { spree_get :for_order_cycle, order_cycle_id: 1 } + before { spree_get :for_order_cycle, format: :json, order_cycle_id: 1 } it "calls order_cycle_enterprises_for() with the existing OrderCycle" do expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("existing OrderCycle") end end context "when a coordinator is provided in params" do - before { spree_get :for_order_cycle, coordinator_id: 1 } + before { spree_get :for_order_cycle, format: :json, coordinator_id: 1 } it "calls order_cycle_enterprises_for() with a new OrderCycle" do expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("new OrderCycle") end end context "when both an order cycle and a coordinator are provided in params" do - before { spree_get :for_order_cycle, order_cycle_id: 1, coordinator_id: 1 } + before { spree_get :for_order_cycle, format: :json, order_cycle_id: 1, coordinator_id: 1 } it "calls order_cycle_enterprises_for() with the existing OrderCycle" do expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("existing OrderCycle") end From 8b7d321dfed6c3184f5555d5c6a26dbf52ea6365 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 10 Apr 2015 16:40:33 +1000 Subject: [PATCH 343/384] Changing delete time to ensure not_deleted scope works --- .../admin/for_order_cycle/enterprise_serializer_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb b/spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb index 3d49c1a2ca..db476164eb 100644 --- a/spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb +++ b/spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb @@ -1,7 +1,7 @@ describe Api::Admin::ForOrderCycle::EnterpriseSerializer do let(:enterprise) { create(:distributor_enterprise) } let!(:product) { create(:simple_product, supplier: enterprise) } - let!(:deleted_product) { create(:simple_product, supplier: enterprise, deleted_at: Time.now) } + let!(:deleted_product) { create(:simple_product, supplier: enterprise, deleted_at: 24.hours.ago ) } let(:serialized_enterprise) { Api::Admin::ForOrderCycle::EnterpriseSerializer.new(enterprise, spree_current_user: enterprise.owner ).to_json } describe "supplied products" do From 24e3c9a9e1dc4a5140dcbd92fdc7a9f4a5007968 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 10 Apr 2015 16:41:10 +1000 Subject: [PATCH 344/384] Adding bugsnag notify block for catching naughty phantom fees --- app/controllers/checkout_controller.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 024c250ede..0c42ceb987 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -19,6 +19,7 @@ class CheckoutController < Spree::CheckoutController def update if @order.update_attributes(object_params) + check_order_for_phantom_fees fire_event('spree.checkout.update') while @order.state != "complete" if @order.state == "payment" @@ -58,6 +59,20 @@ class CheckoutController < Spree::CheckoutController private + def check_order_for_phantom_fees + phantom_fees = @order.adjustments.joins('LEFT OUTER JOIN spree_line_items ON spree_line_items.id = spree_adjustments.source_id'). + where("originator_type = 'EnterpriseFee' AND source_type = 'Spree::LineItem' AND spree_line_items.id IS NULL") + + if phantom_fees.any? + Bugsnag.notify(RuntimeError.new("Phantom Fees"), { + phantom_fees: { + phantom_total: phantom_fees.sum(&:amount).to_s, + phantom_fees: phantom_fees.as_json + } + }) + end + end + # Copied and modified from spree. Remove check for order state, since the state machine is # progressed all the way in one go with the one page checkout. def object_params @@ -110,7 +125,7 @@ class CheckoutController < Spree::CheckoutController last_used_bill_address, last_used_ship_address = find_last_used_addresses(@order.email) preferred_bill_address, preferred_ship_address = spree_current_user.bill_address, spree_current_user.ship_address if spree_current_user.respond_to?(:bill_address) && spree_current_user.respond_to?(:ship_address) @order.bill_address ||= preferred_bill_address || last_used_bill_address || Spree::Address.default - @order.ship_address ||= preferred_ship_address || last_used_ship_address || Spree::Address.default + @order.ship_address ||= preferred_ship_address || last_used_ship_address || Spree::Address.default end def after_payment From 2fd25f6cc43735a18d6abf8613213f1281ab33bb Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 10 Apr 2015 17:42:54 +1000 Subject: [PATCH 345/384] Injecting all enterprises into group page The enterprises have related producers and hubs that need to be dereferenced. Therefore, we need a bigger set of enterprises to be loaded. Injecting all enterprises is simple and doesn't require changes if the set of needed enterprise data is growing in future. --- .../group_enterprises_controller.js.coffee | 3 +-- .../group_page_controller.js.coffee | 14 ++++++++++++++ .../darkswarm/services/map.js.coffee | 8 +++++--- app/views/groups/show.html.haml | 19 +++++++++++++------ 4 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/controllers/group_page_controller.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee index ab8336116f..db1a9a1d8b 100644 --- a/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee @@ -1,5 +1,4 @@ -Darkswarm.controller "GroupEnterprisesCtrl", ($scope, Enterprises, Search, FilterSelectorsService) -> - $scope.Enterprises = Enterprises +Darkswarm.controller "GroupEnterprisesCtrl", ($scope, Search, FilterSelectorsService) -> $scope.totalActive = FilterSelectorsService.totalActive $scope.clearAll = FilterSelectorsService.clearAll $scope.filterText = FilterSelectorsService.filterText diff --git a/app/assets/javascripts/darkswarm/controllers/group_page_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/group_page_controller.js.coffee new file mode 100644 index 0000000000..b30d409b97 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/group_page_controller.js.coffee @@ -0,0 +1,14 @@ +Darkswarm.controller "GroupPageCtrl", ($scope, group_enterprises, Enterprises, MapConfiguration, OfnMap) -> + $scope.Enterprises = Enterprises + + group_enterprises_ids = group_enterprises.map (enterprise) => + enterprise.id + is_in_group = (enterprise) -> + group_enterprises_ids.indexOf(enterprise.id) != -1 + + $scope.group_producers = Enterprises.producers.filter is_in_group + $scope.group_hubs = Enterprises.hubs.filter is_in_group + + $scope.map = angular.copy MapConfiguration.options + $scope.mapMarkers = OfnMap.enterprise_markers group_enterprises + diff --git a/app/assets/javascripts/darkswarm/services/map.js.coffee b/app/assets/javascripts/darkswarm/services/map.js.coffee index 703c3c54bf..7ff9f553f2 100644 --- a/app/assets/javascripts/darkswarm/services/map.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map.js.coffee @@ -1,11 +1,13 @@ -Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter)-> +Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter) -> new class OfnMap constructor: -> - @enterprises = (@extend(enterprise) for enterprise in visibleFilter(Enterprises.enterprises)) + @enterprises = @enterprise_markers(Enterprises.enterprises) + enterprise_markers: (enterprises) -> + @extend(enterprise) for enterprise in visibleFilter(enterprises) # Adding methods to each enterprise - extend: (enterprise)-> + extend: (enterprise) -> new class MapMarker # We're whitelisting attributes because GMaps tries to crawl # our data, and our data is recursive, so it breaks diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 0459b2fc52..625d4f0d6d 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,4 +1,12 @@ -#group-page.row.pad-top +-# inject all enterprises as "enterprises" +-# it could be more efficient to inject only the enterprises that are related to the group += inject_enterprises + +-# inject enterprises in this group +-# further hubs and producers of these enterprises can't be resoleved within this small subset += inject_json_ams "group_enterprises", @group.enterprises, Api::EnterpriseSerializer, active_distributors: @active_distributors + +#group-page.row.pad-top{"ng-controller" => "GroupPageCtrl"} .small-12.columns.pad-top %header .row @@ -22,11 +30,10 @@ %tab{heading: 'Map', active: "active(\'\')", select: "select(\'\')"} - = inject_json_ams "enterprises", @group.enterprises, Api::EnterpriseSerializer, active_distributors: @active_distributors .map-container - %map{"ng-controller" => "MapCtrl", "ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} + %map{"ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} - %markers{models: "OfnMap.enterprises", fit: "true", + %markers{models: "mapMarkers", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} %tab{heading: 'About us', @@ -50,7 +57,7 @@ .small-12.columns .active_table %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", - "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)", + "ng-repeat" => "producer in filteredEnterprises = (group_producers | visible | searchEnterprises:query | taxons:activeTaxons)", "ng-controller" => "GroupEnterpriseNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", id: "{{producer.hash}}"} @@ -77,7 +84,7 @@ .small-12.columns .active_table %hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}", - "ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", + "ng-repeat" => "hub in filteredEnterprises = (group_hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "ng-controller" => "GroupEnterpriseNodeCtrl"} .small-12.columns From 02f8f293da0e60e4ccff46b0b2d0e9ac757ad83f Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 18:32:04 +1000 Subject: [PATCH 346/384] Only allow managers or coordinator to add/remove fees from exchanges --- .../order_cycles/_exchange_form.html.haml | 4 +- .../order_cycle_form_applicator.rb | 22 ++- .../order_cycle_form_applicator_spec.rb | 184 ++++++++++++------ 3 files changed, 136 insertions(+), 74 deletions(-) diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index d26691cc30..7f0fb983f9 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -13,7 +13,7 @@ %br/ = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', 'placeholder' => 'Pick-up instructions', 'ng-model' => 'exchange.pickup_instructions', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator' %td.fees - %ol + %ol{ ng: { show: 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } } %li{'ng-repeat' => 'enterprise_fee in exchange.enterprise_fees'} = select_tag 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', nil, {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', 'ng-model' => 'enterprise_fee.enterprise_id', 'ng-options' => 'enterprise.id as enterprise.name for enterprise in enterprisesWithFees()'} @@ -21,6 +21,6 @@ = link_to 'Remove', '#', {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_remove', 'ng-click' => 'removeExchangeFee($event, exchange, $index)'} - = f.submit 'Add fee', 'ng-click' => 'addExchangeFee($event, exchange)' + = f.submit 'Add fee', 'ng-click' => 'addExchangeFee($event, exchange)', 'ng-hide' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator' %td.actions %a{'ng-click' => 'removeExchange($event, exchange)', :class => "icon-trash no-text remove-exchange"} diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index c23df65e8c..3447699538 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -60,7 +60,7 @@ module OpenFoodNetwork attrs = attrs.reverse_merge(:sender_id => sender_id, :receiver_id => receiver_id, :incoming => incoming) exchange = @order_cycle.exchanges.build attrs - if permission_for exchange + if manages_coordinator? exchange.save! @touched_exchanges << exchange end @@ -69,6 +69,12 @@ module OpenFoodNetwork def update_exchange(sender_id, receiver_id, incoming, attrs={}) exchange = @order_cycle.exchanges.where(:sender_id => sender_id, :receiver_id => receiver_id, :incoming => incoming).first + unless manages_coordinator? || manager_for(exchange) + attrs.delete :enterprise_fee_ids + attrs.delete :pickup_time + attrs.delete :pickup_instructions + end + if permission_for exchange exchange.update_attributes!(attrs) @touched_exchanges << exchange @@ -76,8 +82,8 @@ module OpenFoodNetwork end def destroy_untouched_exchanges - if user_manages_coordinator? - with_permission(untouched_exchanges).each(&:destroy) + if manages_coordinator? + untouched_exchanges.each(&:destroy) end end @@ -86,8 +92,8 @@ module OpenFoodNetwork @order_cycle.exchanges.reject { |ex| touched_exchange_ids.include? ex.id } end - def with_permission(exchanges) - exchanges.select { |ex| permission_for(ex) } + def manager_for(exchange) + Enterprise.managed_by(@spree_current_user).include? exchange.participant end def permission_for(exchange) @@ -100,9 +106,9 @@ module OpenFoodNetwork order_cycle_enterprises_for(@order_cycle) end - def user_manages_coordinator? - return @user_manages_coordinator unless @user_manages_coordinator.nil? - @user_manages_coordinator = Enterprise.managed_by(@spree_current_user).include? @order_cycle.coordinator + def manages_coordinator? + return @manages_coordinator unless @manages_coordinator.nil? + @manages_coordinator = Enterprise.managed_by(@spree_current_user).include? @order_cycle.coordinator end def editable_variant_ids_for_incoming_exchange_between(sender, receiver) diff --git a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb index e7761c6193..6a03964039 100644 --- a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb +++ b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb @@ -120,26 +120,27 @@ module OpenFoodNetwork applicator.send(:untouched_exchanges).should == [] end - it "does not destroy exchanges involving enterprises it does not have permission to touch" do - applicator = OrderCycleFormApplicator.new(nil, user) - applicator.stub(:user_manages_coordinator?) { true } - exchanges = double(:exchanges) - permitted_exchanges = [double(:exchange), double(:exchange)] + context "as a manager of the coordinator" do + let(:applicator) { OrderCycleFormApplicator.new(nil, user) } + before { applicator.stub(:manages_coordinator?) { true } } - applicator.should_receive(:with_permission).with(exchanges) { permitted_exchanges } - applicator.stub(:untouched_exchanges) { exchanges } - permitted_exchanges.each { |ex| ex.should_receive(:destroy) } + it "destroys exchanges" do + exchanges = [double(:exchange), double(:exchange)] + expect(applicator).to receive(:untouched_exchanges) { exchanges } + exchanges.each { |ex| expect(ex).to receive(:destroy) } - applicator.send(:destroy_untouched_exchanges) + applicator.send(:destroy_untouched_exchanges) + end end - it "does not destroy any exchanges when it is not the coordinator" do - applicator = OrderCycleFormApplicator.new(nil, user) - applicator.stub(:user_manages_coordinator?) { false } + context "as a non-manager of the coordinator" do + let(:applicator) { OrderCycleFormApplicator.new(nil, user) } + before { applicator.stub(:manages_coordinator?) { false } } - expect(applicator).to_not receive(:with_permission) - - applicator.send(:destroy_untouched_exchanges) + it "does not destroy any exchanges" do + expect(applicator).to_not receive(:with_permission) + applicator.send(:destroy_untouched_exchanges) + end end end @@ -255,15 +256,6 @@ module OpenFoodNetwork applicator.send(:permission_for, ex).should be_false end end - - describe "filtering many exchanges" do - it "returns exchanges involving enterprises we have permission to touch" do - ex1, ex2 = double(:exchange), double(:exchange) - applicator = OrderCycleFormApplicator.new(nil, user) - applicator.stub(:permission_for).and_return(true, false) - applicator.send(:with_permission, [ex1, ex2]).should == [ex1] - end - end end end @@ -285,55 +277,119 @@ module OpenFoodNetwork applicator.send(:exchange_exists?, 999999, 888888, exchange.incoming).should be_false end - it "adds exchanges" do - sender = FactoryGirl.create(:enterprise) - receiver = FactoryGirl.create(:enterprise) - oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, user) - applicator.stub(:permitted_enterprises) { [sender, receiver] } - incoming = true - variant1 = FactoryGirl.create(:variant) - variant2 = FactoryGirl.create(:variant) - enterprise_fee1 = FactoryGirl.create(:enterprise_fee) - enterprise_fee2 = FactoryGirl.create(:enterprise_fee) + describe "adding exchanges" do + let!(:sender) { create(:enterprise) } + let!(:receiver) { create(:enterprise) } + let!(:oc) { create(:simple_order_cycle) } + let!(:applicator) { OrderCycleFormApplicator.new(oc, user) } + let!(:incoming) { true } + let!(:variant1) { create(:variant) } + let!(:variant2) { create(:variant) } + let!(:enterprise_fee1) { create(:enterprise_fee) } + let!(:enterprise_fee2) { create(:enterprise_fee) } - applicator.send(:touched_exchanges=, []) - applicator.send(:add_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant2.id], :enterprise_fee_ids => [enterprise_fee1.id, enterprise_fee2.id]}) + context "as a manager of the coorindator" do + before do + allow(applicator).to receive(:manages_coordinator?) { true } + applicator.send(:touched_exchanges=, []) + applicator.send(:add_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant2.id], :enterprise_fee_ids => [enterprise_fee1.id, enterprise_fee2.id]}) + end - exchange = Exchange.last - exchange.sender.should == sender - exchange.receiver.should == receiver - exchange.incoming.should == incoming - exchange.variants.sort.should == [variant1, variant2].sort - exchange.enterprise_fees.sort.should == [enterprise_fee1, enterprise_fee2].sort + it "adds new exchanges" do + exchange = Exchange.last + expect(exchange.sender).to eq sender + expect(exchange.receiver).to eq receiver + expect(exchange.incoming).to eq incoming + expect(exchange.variants.sort).to eq [variant1, variant2].sort + expect(exchange.enterprise_fees.sort).to eq [enterprise_fee1, enterprise_fee2].sort - applicator.send(:touched_exchanges).should == [exchange] + applicator.send(:touched_exchanges).should == [exchange] + end + end + + context "as a user which does not manage the coorindator" do + before do + allow(applicator).to receive(:manages_coordinator?) { false } + applicator.send(:add_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant2.id], :enterprise_fee_ids => [enterprise_fee1.id, enterprise_fee2.id]}) + end + + it "does not add new exchanges" do + expect(Exchange.last).to be_nil + end + end end - it "updates exchanges" do - sender = FactoryGirl.create(:enterprise) - receiver = FactoryGirl.create(:enterprise) - oc = FactoryGirl.create(:simple_order_cycle) - applicator = OrderCycleFormApplicator.new(oc, user) - applicator.stub(:permitted_enterprises) { [sender, receiver] } + describe "updating exchanges" do + let!(:sender) { create(:enterprise) } + let!(:receiver) { create(:enterprise) } + let!(:oc) { create(:simple_order_cycle) } + let!(:applicator) { OrderCycleFormApplicator.new(oc, user) } + let!(:incoming) { true } + let!(:variant1) { create(:variant) } + let!(:variant2) { create(:variant) } + let!(:variant3) { create(:variant) } + let!(:enterprise_fee1) { create(:enterprise_fee) } + let!(:enterprise_fee2) { create(:enterprise_fee) } + let!(:enterprise_fee3) { create(:enterprise_fee) } - incoming = true - variant1 = FactoryGirl.create(:variant) - variant2 = FactoryGirl.create(:variant) - variant3 = FactoryGirl.create(:variant) - enterprise_fee1 = FactoryGirl.create(:enterprise_fee) - enterprise_fee2 = FactoryGirl.create(:enterprise_fee) - enterprise_fee3 = FactoryGirl.create(:enterprise_fee) + let!(:exchange) { create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, incoming: incoming, variant_ids: [variant1.id, variant2.id], enterprise_fee_ids: [enterprise_fee1.id, enterprise_fee2.id]) } - exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, incoming: incoming, variant_ids: [variant1.id, variant2.id], enterprise_fee_ids: [enterprise_fee1.id, enterprise_fee2.id]) + context "as a manager of the coorindator" do + before do + allow(applicator).to receive(:manages_coordinator?) { true } + allow(applicator).to receive(:manager_for) { false } + allow(applicator).to receive(:permission_for) { true } + applicator.send(:touched_exchanges=, []) + applicator.send(:update_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant3.id], :enterprise_fee_ids => [enterprise_fee2.id, enterprise_fee3.id], :pickup_time => 'New Pickup Time', :pickup_instructions => 'New Pickup Instructions'}) + end - applicator.send(:touched_exchanges=, []) - applicator.send(:update_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant3.id], :enterprise_fee_ids => [enterprise_fee2.id, enterprise_fee3.id]}) + it "updates the variants, enterprise fees and pickup information of the exchange" do + exchange.reload + expect(exchange.variants.sort).to eq [variant1, variant3].sort + expect(exchange.enterprise_fees.sort).to eq [enterprise_fee2, enterprise_fee3] + expect(exchange.pickup_time).to eq 'New Pickup Time' + expect(exchange.pickup_instructions).to eq 'New Pickup Instructions' + expect(applicator.send(:touched_exchanges)).to eq [exchange] + end + end - exchange.reload - exchange.variants.sort.should == [variant1, variant3].sort - exchange.enterprise_fees.sort.should == [enterprise_fee2, enterprise_fee3] - applicator.send(:touched_exchanges).should == [exchange] + context "as a manager of the participating enterprise" do + before do + allow(applicator).to receive(:manages_coordinator?) { false } + allow(applicator).to receive(:manager_for) { true } + allow(applicator).to receive(:permission_for) { true } + applicator.send(:touched_exchanges=, []) + applicator.send(:update_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant3.id], :enterprise_fee_ids => [enterprise_fee2.id, enterprise_fee3.id], :pickup_time => 'New Pickup Time', :pickup_instructions => 'New Pickup Instructions'}) + end + + it "updates the variants, enterprise fees and pickup information of the exchange" do + exchange.reload + expect(exchange.variants.sort).to eq [variant1, variant3].sort + expect(exchange.enterprise_fees.sort).to eq [enterprise_fee2, enterprise_fee3] + expect(exchange.pickup_time).to eq 'New Pickup Time' + expect(exchange.pickup_instructions).to eq 'New Pickup Instructions' + expect(applicator.send(:touched_exchanges)).to eq [exchange] + end + end + + context "where the participating enterprise is permitted for the user" do + before do + allow(applicator).to receive(:manages_coordinator?) { false } + allow(applicator).to receive(:manager_for) { false } + allow(applicator).to receive(:permission_for) { true } + applicator.send(:touched_exchanges=, []) + applicator.send(:update_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant3.id], :enterprise_fee_ids => [enterprise_fee2.id, enterprise_fee3.id], :pickup_time => 'New Pickup Time', :pickup_instructions => 'New Pickup Instructions'}) + end + + it "updates the variants in the exchange, but not the fees or pickup information" do + exchange.reload + expect(exchange.variants.sort).to eq [variant1, variant3].sort + expect(exchange.enterprise_fees.sort).to eq [enterprise_fee1, enterprise_fee2] + expect(exchange.pickup_time).to_not eq 'New Pickup Time' + expect(exchange.pickup_instructions).to_not eq 'New Pickup Instructions' + expect(applicator.send(:touched_exchanges)).to eq [exchange] + end + end end it "does not add exchanges it is not permitted to touch" do From 26d55baa35ade2f61337d916bbb68b9f0228843f Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 21:11:10 +1000 Subject: [PATCH 347/384] Moving order cycle permissions to their own permissions class --- .../admin/enterprise_fees_controller.rb | 2 +- .../admin/enterprises_controller.rb | 2 +- app/helpers/order_cycles_helper.rb | 2 +- .../api/admin/exchange_serializer.rb | 8 +- .../api/admin/order_cycle_serializer.rb | 20 +- app/views/admin/order_cycles/_row.html.haml | 4 +- .../order_cycle_form_applicator.rb | 14 +- .../order_cycle_permissions.rb | 240 ++++++ lib/open_food_network/permissions.rb | 230 ----- .../admin/enterprises_controller_spec.rb | 21 +- .../order_cycle_permissions_spec.rb | 797 ++++++++++++++++++ .../lib/open_food_network/permissions_spec.rb | 792 ----------------- .../admin/exchange_serializer_spec.rb | 9 +- 13 files changed, 1078 insertions(+), 1063 deletions(-) create mode 100644 lib/open_food_network/order_cycle_permissions.rb create mode 100644 spec/lib/open_food_network/order_cycle_permissions_spec.rb diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index 13e27e05aa..f665bc1c37 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -79,7 +79,7 @@ module Admin order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id] coordinator = Enterprise.find_by_id(params[:coordinator_id]) if params[:coordinator_id] order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present? - enterprises = OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) + enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for return EnterpriseFee.for_enterprises(enterprises).order('enterprise_id', 'fee_type', 'name') else collection = EnterpriseFee.managed_by(spree_current_user).order('enterprise_id', 'fee_type', 'name') diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index bb13bb6739..9776f23e69 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -108,7 +108,7 @@ module Admin order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id] coordinator = Enterprise.find_by_id(params[:coordinator_id]) if params[:coordinator_id] order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present? - return OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) + return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for else # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now OpenFoodNetwork::Permissions.new(spree_current_user). diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index aa7b52d19a..acbe28720f 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -4,7 +4,7 @@ module OrderCyclesHelper end def permitted_enterprises_for(order_cycle) - OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) + OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for end def permitted_producer_enterprises_for(order_cycle) diff --git a/app/serializers/api/admin/exchange_serializer.rb b/app/serializers/api/admin/exchange_serializer.rb index 518d865003..64c2c08cd4 100644 --- a/app/serializers/api/admin/exchange_serializer.rb +++ b/app/serializers/api/admin/exchange_serializer.rb @@ -6,11 +6,11 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer def variants permitted = Spree::Variant.where("1=0") if object.incoming - permitted = OpenFoodNetwork::Permissions.new(options[:current_user]). - visible_variants_for_incoming_exchanges_between(object.sender, object.receiver, order_cycle: object.order_cycle) + permitted = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle). + visible_variants_for_incoming_exchanges_from(object.sender) else - permitted = OpenFoodNetwork::Permissions.new(options[:current_user]). - visible_variants_for_outgoing_exchanges_between(object.sender, object.receiver, order_cycle: object.order_cycle) + permitted = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle). + visible_variants_for_outgoing_exchanges_to(object.receiver) end Hash[ object.variants.merge(permitted).map { |v| [v.id, true] } ] end diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index d907d3ef6d..795daf85bc 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -19,7 +19,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer end def exchanges - scoped_exchanges = OpenFoodNetwork::Permissions.new(options[:current_user]).order_cycle_exchanges(object).order('id ASC') + scoped_exchanges = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).order_cycle_exchanges.order('id ASC') ActiveModel::ArraySerializer.new(scoped_exchanges, {each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user] }) end @@ -27,10 +27,10 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be editable within incoming exchanges from that enterprise editable = {} - permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(object) + permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) + enterprises = permissions.order_cycle_enterprises_for enterprises.each do |enterprise| - variants = permissions.editable_variants_for_incoming_exchanges_between(enterprise, object.coordinator, order_cycle: object).pluck(:id) + variants = permissions.editable_variants_for_incoming_exchanges_from(enterprise).pluck(:id) editable[enterprise.id] = variants if variants.any? end editable @@ -40,10 +40,10 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be editable within incoming exchanges from that enterprise editable = {} - permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(object) + permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) + enterprises = permissions.order_cycle_enterprises_for enterprises.each do |enterprise| - variants = permissions.editable_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) + variants = permissions.editable_variants_for_outgoing_exchanges_to(enterprise).pluck(:id) editable[enterprise.id] = variants if variants.any? end editable @@ -53,10 +53,10 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be visible within outgoing exchanges from that enterprise visible = {} - permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(object) + permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) + enterprises = permissions.order_cycle_enterprises_for enterprises.each do |enterprise| - variants = permissions.visible_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) + variants = permissions.visible_variants_for_outgoing_exchanges_to(enterprise).pluck(:id) visible[enterprise.id] = variants if variants.any? end visible diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index f30c740f03..1af364440e 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -8,7 +8,7 @@ - unless order_cycles_simple_index %td.suppliers - - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle)) + - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for) - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 %span.with-tip{'data-powertip' => supplier_list} @@ -18,7 +18,7 @@ = supplier_list %td= order_cycle.coordinator.name %td.distributors - - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle)) + - distributors = order_cycle.distributors.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for) - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 %span.with-tip{'data-powertip' => distributor_list} diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index 3447699538..81e070049c 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -102,8 +102,8 @@ module OpenFoodNetwork def permitted_enterprises return @permitted_enterprises unless @permitted_enterprises.nil? - @permitted_enterprises = OpenFoodNetwork::Permissions.new(@spree_current_user). - order_cycle_enterprises_for(@order_cycle) + @permitted_enterprises = OpenFoodNetwork::OrderCyclePermissions. + new(@spree_current_user, @order_cycle).order_cycle_enterprises_for end def manages_coordinator? @@ -112,15 +112,13 @@ module OpenFoodNetwork end def editable_variant_ids_for_incoming_exchange_between(sender, receiver) - OpenFoodNetwork::Permissions.new(@spree_current_user). - editable_variants_for_incoming_exchanges_between(sender, receiver, order_cycle: @order_cycle). - pluck(:id) + OpenFoodNetwork::OrderCyclePermissions.new(@spree_current_user, @order_cycle). + editable_variants_for_incoming_exchanges_from(sender).pluck(:id) end def editable_variant_ids_for_outgoing_exchange_between(sender, receiver) - OpenFoodNetwork::Permissions.new(@spree_current_user). - editable_variants_for_outgoing_exchanges_between(sender, receiver, order_cycle: @order_cycle). - pluck(:id) + OpenFoodNetwork::OrderCyclePermissions.new(@spree_current_user, @order_cycle). + editable_variants_for_outgoing_exchanges_to(receiver).pluck(:id) end def find_incoming_exchange(attrs) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb new file mode 100644 index 0000000000..80c7bb9f19 --- /dev/null +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -0,0 +1,240 @@ +module OpenFoodNetwork + # Class which is used for determining the permissions around a single order cycle and user + # both of which are set at initialization + class OrderCyclePermissions < Permissions + def initialize(user, order_cycle) + super(user) + @order_cycle = order_cycle + @coordinator = order_cycle.andand.coordinator + end + + # List of any enterprises whose exchanges I should be able to see in order_cycle + # NOTE: the enterprises a given user can see actually in the OC interface depend on the relationships + # of their enterprises to the coordinator of the order cycle, rather than on the order cycle itself + def order_cycle_enterprises_for + return Enterprise.where("1=0") unless @coordinator.present? + if managed_enterprises.include? @coordinator + coordinator_permitted = [@coordinator] + all_active = [] + + if @coordinator.sells == "any" + # If the coordinator sells any, relationships come into play + granting(:add_to_order_cycle, to: [@coordinator]).pluck(:id).each do |enterprise_id| + coordinator_permitted << enterprise_id + end + + # As a safety net, we should load all of the enterprises invloved in existing exchanges in this order cycle + all_active = @order_cycle.suppliers.pluck(:id) | @order_cycle.distributors.pluck(:id) + end + + Enterprise.where(id: coordinator_permitted | all_active) + else + # Any enterprises that I manage directly, which have granted P-OC to the coordinator + managed_permitted = granting(:add_to_order_cycle, to: [@coordinator], scope: managed_enterprises_in(@order_cycle) ).pluck(:id) + + # Any hubs in this OC that have been granted P-OC by producers I manage in this OC + hubs_permitted = granted(:add_to_order_cycle, by: managed_producers_in(@order_cycle), scope: @order_cycle.distributors).pluck(:id) + + # Any hubs in this OC that have granted P-OC to producers I manage in this OC + hubs_permitting = granting(:add_to_order_cycle, to: managed_producers_in(@order_cycle), scope: @order_cycle.distributors).pluck(:id) + + # Any producers in this OC that have been granted P-OC by hubs I manage in this OC + producers_permitted = granted(:add_to_order_cycle, by: managed_hubs_in(@order_cycle), scope: @order_cycle.suppliers).pluck(:id) + + # Any producers in this OC that have granted P-OC to hubs I manage in this OC + producers_permitting = granting(:add_to_order_cycle, to: managed_hubs_in(@order_cycle), scope: @order_cycle.suppliers).pluck(:id) + + managed_active = [] + hubs_active = [] + producers_active = [] + if @order_cycle + # TODO: Remove this when all P-OC are sorted out + # Any enterprises that I manage that are already in the order_cycle + managed_active = managed_enterprises_in(@order_cycle).pluck(:id) + + # TODO: Remove this when all P-OC are sorted out + # Any hubs that currently have outgoing exchanges distributing variants of producers I manage + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + active_exchanges = @order_cycle.exchanges.outgoing.with_any_variant(variants) + hubs_active = active_exchanges.map(&:receiver_id) + + # TODO: Remove this when all P-OC are sorted out + # Any producers of variants that hubs I manage are currently distributing in this OC + variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", managed_hubs_in(@order_cycle), @order_cycle).pluck(:id).uniq + products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq + producers_active = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq + end + + ids = managed_permitted | hubs_permitted | hubs_permitting | producers_permitted | producers_permitting | managed_active | hubs_active | producers_active + + Enterprise.where(id: ids.sort ) + end + end + + # Find the exchanges of an order cycle that an admin can manage + def order_cycle_exchanges + ids = order_cycle_exchange_ids_involving_my_enterprises | + order_cycle_exchange_ids_distributing_my_variants | + order_cycle_exchange_ids_with_distributable_variants + + Exchange.where(id: ids, order_cycle_id: @order_cycle) + end + + # Find the variants that a user can POTENTIALLY see within incoming exchanges + def visible_variants_for_incoming_exchanges_from(producer) + return Spree::Variant.where("1=0") unless @order_cycle + if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + # All variants belonging to the producer + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + # All variants of the producer if it has granted P-OC to any of my managed hubs that are in this order cycle + permitted = EnterpriseRelationship.permitting(managed_hubs_in(@order_cycle)). + permitted_by(producer).with_permission(:add_to_order_cycle).present? + if permitted + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + Spree::Variant.where("1=0") + end + end + end + + # Find the variants that a user can edit within incoming exchanges + def editable_variants_for_incoming_exchanges_from(producer) + return Spree::Variant.where("1=0") unless @order_cycle + if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + # All variants belonging to the producer + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + Spree::Variant.where("1=0") + end + end + + # Find the variants that a user is permitted see within outgoing exchanges + # Note that this does not determine whether they actually appear in outgoing exchanges + # as this requires first that the variant is included in an incoming exchange + def visible_variants_for_outgoing_exchanges_to(hub) + return Spree::Variant.where("1=0") unless @order_cycle + if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + # Any variants produced by the coordinator, for outgoing exchanges with itself + coordinator_variants = [] + if hub == @coordinator + coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', @coordinator) + end + + # Any variants of any producers that have granted the hub P-OC + producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = [] + @order_cycle.exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| + active_variants = exchange.variants + end + + Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) + else + # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub + producers = granting(:add_to_order_cycle, to: [hub], scope: managed_producers_in(@order_cycle)) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = Spree::Variant.joins(:exchanges, :product). + where("exchanges.receiver_id = (?) AND spree_products.supplier_id IN (?) AND incoming = 'f'", hub, managed_enterprises.is_primary_producer) + + Spree::Variant.where(id: permitted_variants | active_variants) + end + end + + # Find the variants that a user is permitted edit within outgoing exchanges + def editable_variants_for_outgoing_exchanges_to(hub) + return Spree::Variant.where("1=0") unless @order_cycle + if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + # Any variants produced by the coordinator, for outgoing exchanges with itself + coordinator_variants = [] + if hub == @coordinator + coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', @coordinator) + end + + # Any variants of any producers that have granted the hub P-OC + producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = [] + @order_cycle.exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| + active_variants = exchange.variants + end + + Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) + else + # Any of my managed producers in this order cycle granted P-OC by the hub + granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_producers_in(@order_cycle)) + + # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub + granting_producers = granting(:add_to_order_cycle, to: [hub], scope: granted_producers) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', granting_producers) + + Spree::Variant.where(id: permitted_variants) + end + end + + + private + + def managed_enterprises_in(order_cycle) + managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors) + end + + def managed_hubs_in(order_cycle) + managed_enterprises_in(order_cycle).is_hub + end + + def managed_producers_in(order_cycle) + managed_enterprises_in(order_cycle).is_primary_producer + end + + def order_cycle_exchange_ids_involving_my_enterprises + # Any exchanges that my managed enterprises are involved in directly + @order_cycle.exchanges.involving(managed_enterprises).pluck :id + end + + def order_cycle_exchange_ids_with_distributable_variants + # Find my managed hubs in this order cycle + hubs = managed_hubs_in(@order_cycle) + # Any incoming exchange where the producer has granted P-OC to one or more of those hubs + producers = granting(:add_to_order_cycle, to: hubs, scope: Enterprise.is_primary_producer).pluck :id + permitted_exchanges = @order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id + + # TODO: remove active_exchanges when we think it is safe to do so + # active_exchanges is for backward compatability, before we restricted variants in each + # outgoing exchange to those where the producer had granted P-OC to the distributor + # For any of my managed hubs in this OC, any incoming exchanges supplying variants in my outgoing exchanges + variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", hubs, @order_cycle).pluck(:id).uniq + products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq + producers = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq + active_exchanges = @order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id + + permitted_exchanges | active_exchanges + end + + def order_cycle_exchange_ids_distributing_my_variants + # Find my producers in this order cycle + producers = managed_producers_in(@order_cycle).pluck :id + # Any outgoing exchange where the distributor has been granted P-OC by one or more of those producers + hubs = granted(:add_to_order_cycle, by: producers, scope: Enterprise.is_hub) + permitted_exchanges = @order_cycle.exchanges.outgoing.where(receiver_id: hubs).pluck :id + + # TODO: remove active_exchanges when we think it is safe to do so + # active_exchanges is for backward compatability, before we restricted variants in each + # outgoing exchange to those where the producer had granted P-OC to the distributor + # For any of my managed producers, any outgoing exchanges with their variants + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + active_exchanges = @order_cycle.exchanges.outgoing.with_any_variant(variants).pluck :id + + permitted_exchanges | active_exchanges + end + end +end diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 9a98d7a750..16319ced97 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -15,73 +15,6 @@ module OpenFoodNetwork managed_and_related_enterprises_with :add_to_order_cycle end - # List of any enterprises whose exchanges I should be able to see in order_cycle - # NOTE: the enterprises a given user can see actually in the OC interface depend on the relationships - # of their enterprises to the coordinator of the order cycle, rather than on the order cycle itself - # (until such time as we implement friends of friends) - def order_cycle_enterprises_for(order_cycle) - return Enterprise.where("1=0") unless order_cycle.andand.coordinator.present? - coordinator = order_cycle.coordinator - - if managed_enterprises.include? coordinator - coordinator_permitted = [coordinator] - all_active = [] - - if coordinator.sells == "any" - # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) - # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) - # If the coordinator sells any, relationships come into play - granting(:add_to_order_cycle, to: [coordinator]).pluck(:id).each do |enterprise_id| - coordinator_permitted << enterprise_id - end - - all_active = order_cycle.suppliers.pluck(:id) | order_cycle.distributors.pluck(:id) - end - - Enterprise.where(id: coordinator_permitted | all_active) - else - # Any enterprises that I manage directly, which have granted P-OC to the coordinator - managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises_in(order_cycle) ).pluck(:id) - - # Any hubs in this OC that have been granted P-OC by producers I manage in this OC - hubs_permitted = granted(:add_to_order_cycle, by: managed_producers_in(order_cycle), scope: order_cycle.distributors).pluck(:id) - - # Any hubs in this OC that have granted P-OC to producers I manage in this OC - hubs_permitting = granting(:add_to_order_cycle, to: managed_producers_in(order_cycle), scope: order_cycle.distributors).pluck(:id) - - # Any producers in this OC that have been granted P-OC by hubs I manage in this OC - producers_permitted = granted(:add_to_order_cycle, by: managed_hubs_in(order_cycle), scope: order_cycle.suppliers).pluck(:id) - - # Any producers in this OC that have granted P-OC to hubs I manage in this OC - producers_permitting = granting(:add_to_order_cycle, to: managed_hubs_in(order_cycle), scope: order_cycle.suppliers).pluck(:id) - - managed_active = [] - hubs_active = [] - producers_active = [] - if order_cycle - # TODO: Remove this when all P-OC are sorted out - # Any enterprises that I manage that are already in the order_cycle - managed_active = managed_enterprises_in(order_cycle).pluck(:id) - - # TODO: Remove this when all P-OC are sorted out - # Any hubs that currently have outgoing exchanges distributing variants of producers I manage - variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) - active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants) - hubs_active = active_exchanges.map(&:receiver_id) - - # TODO: Remove this when all P-OC are sorted out - # Any producers of variants that hubs I manage are currently distributing in this OC - variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", managed_hubs_in(order_cycle), order_cycle).pluck(:id).uniq - products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq - producers_active = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq - end - - ids = managed_permitted | hubs_permitted | hubs_permitting | producers_permitted | producers_permitting | managed_active | hubs_active | producers_active - - Enterprise.where(id: ids.sort ) - end - end - # Find enterprises for which an admin is allowed to edit their profile def editable_enterprises managed_and_related_enterprises_with :edit_profile @@ -123,116 +56,6 @@ module OpenFoodNetwork permissions end - # Find the exchanges of an order cycle that an admin can manage - def order_cycle_exchanges(order_cycle) - ids = order_cycle_exchange_ids_involving_my_enterprises(order_cycle) | - order_cycle_exchange_ids_distributing_my_variants(order_cycle) | - order_cycle_exchange_ids_with_distributable_variants(order_cycle) - - Exchange.where(id: ids, order_cycle_id: order_cycle) - end - - # Find the variants that a user can POTENTIALLY see within incoming exchanges - def visible_variants_for_incoming_exchanges_between(producer, coordinator, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # All variants belonging to the producer - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) - else - # All variants of the producer if it has granted P-OC to any of my managed hubs that are in this order cycle - permitted = EnterpriseRelationship.permitting(managed_hubs_in(options[:order_cycle])). - permitted_by(producer).with_permission(:add_to_order_cycle).present? - if permitted - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) - else - Spree::Variant.where("1=0") - end - end - end - - # Find the variants that a user can edit within incoming exchanges - def editable_variants_for_incoming_exchanges_between(producer, coordinator, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # All variants belonging to the producer - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) - else - Spree::Variant.where("1=0") - end - end - - # Find the variants that a user is permitted see within outgoing exchanges - # Note that this does not determine whether they actually appear in outgoing exchanges - # as this requires first that the variant is included in an incoming exchange - def visible_variants_for_outgoing_exchanges_between(coordinator, hub, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # Any variants produced by the coordinator, for outgoing exchanges with itself - coordinator_variants = [] - if hub == coordinator - coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', coordinator) - end - - # Any variants of any producers that have granted the hub P-OC - producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - - # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break - # TODO: Remove this when all P-OC are sorted out - active_variants = [] - options[:order_cycle].exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| - active_variants = exchange.variants - end - - Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) - else - # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub - producers = granting(:add_to_order_cycle, to: [hub], scope: managed_producers_in(options[:order_cycle])) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - - # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break - # TODO: Remove this when all P-OC are sorted out - active_variants = Spree::Variant.joins(:exchanges, :product). - where("exchanges.receiver_id = (?) AND spree_products.supplier_id IN (?) AND incoming = 'f'", hub, managed_enterprises.is_primary_producer) - - Spree::Variant.where(id: permitted_variants | active_variants) - end - end - - # Find the variants that a user is permitted edit within outgoing exchanges - def editable_variants_for_outgoing_exchanges_between(coordinator, hub, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # Any variants produced by the coordinator, for outgoing exchanges with itself - coordinator_variants = [] - if hub == coordinator - coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', coordinator) - end - - # Any variants of any producers that have granted the hub P-OC - producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - - # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break - # TODO: Remove this when all P-OC are sorted out - active_variants = [] - options[:order_cycle].exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| - active_variants = exchange.variants - end - - Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) - else - # Any of my managed producers in this order cycle granted P-OC by the hub - granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_producers_in(options[:order_cycle])) - - # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub - granting_producers = granting(:add_to_order_cycle, to: [hub], scope: granted_producers) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', granting_producers) - - Spree::Variant.where(id: permitted_variants ) - end - end - def managed_products managed_enterprise_products_ids = managed_enterprise_products.pluck :id permitted_enterprise_products_ids = related_enterprise_products.pluck :id @@ -261,18 +84,6 @@ module OpenFoodNetwork Enterprise.managed_by(@user) end - def managed_enterprises_in(order_cycle) - managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors) - end - - def managed_hubs_in(order_cycle) - managed_enterprises_in(order_cycle).is_hub - end - - def managed_producers_in(order_cycle) - managed_enterprises_in(order_cycle).is_primary_producer - end - def related_enterprises_with(permission) parent_ids = EnterpriseRelationship. permitting(managed_enterprises). @@ -307,46 +118,5 @@ module OpenFoodNetwork def related_enterprise_products Spree::Product.where('supplier_id IN (?)', related_enterprises_with(:manage_products)) end - - def order_cycle_exchange_ids_involving_my_enterprises(order_cycle) - # Any exchanges that my managed enterprises are involved in directly - order_cycle.exchanges.involving(managed_enterprises).pluck :id - end - - def order_cycle_exchange_ids_with_distributable_variants(order_cycle) - # Find my managed hubs in this order cycle - hubs = managed_hubs_in(order_cycle) - # Any incoming exchange where the producer has granted P-OC to one or more of those hubs - producers = granting(:add_to_order_cycle, to: hubs, scope: Enterprise.is_primary_producer).pluck :id - permitted_exchanges = order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id - - # TODO: remove active_exchanges when we think it is safe to do so - # active_exchanges is for backward compatability, before we restricted variants in each - # outgoing exchange to those where the producer had granted P-OC to the distributor - # For any of my managed hubs in this OC, any incoming exchanges supplying variants in my outgoing exchanges - variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", hubs, order_cycle).pluck(:id).uniq - products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq - producers = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq - active_exchanges = order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id - - permitted_exchanges | active_exchanges - end - - def order_cycle_exchange_ids_distributing_my_variants(order_cycle) - # Find my producers in this order cycle - producers = managed_producers_in(order_cycle).pluck :id - # Any outgoing exchange where the distributor has been granted P-OC by one or more of those producers - hubs = granted(:add_to_order_cycle, by: producers, scope: Enterprise.is_hub) - permitted_exchanges = order_cycle.exchanges.outgoing.where(receiver_id: hubs).pluck :id - - # TODO: remove active_exchanges when we think it is safe to do so - # active_exchanges is for backward compatability, before we restricted variants in each - # outgoing exchange to those where the producer had granted P-OC to the distributor - # For any of my managed producers, any outgoing exchanges with their variants - variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants).pluck :id - - permitted_exchanges | active_exchanges - end end end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 53edeae6b7..1b5849ef9f 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'open_food_network/order_cycle_permissions' module Admin describe EnterprisesController do @@ -418,36 +419,36 @@ module Admin Enterprise.stub find_by_id: "existing Enterprise" OrderCycle.stub new: "new OrderCycle" - OpenFoodNetwork::Permissions.stub(:new) { permission_mock } - allow(permission_mock).to receive(:order_cycle_enterprises_for) + allow(OpenFoodNetwork::OrderCyclePermissions).to receive(:new) { permission_mock } + allow(permission_mock).to receive(:order_cycle_enterprises_for) { [] } allow(ActiveModel::ArraySerializer).to receive(:new) { "" } end context "when no order_cycle or coordinator is provided in params" do before { spree_get :for_order_cycle, format: :json } - it "returns an empty scope" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(nil) + it "initializes permissions with nil" do + expect(OpenFoodNetwork::OrderCyclePermissions).to have_received(:new).with(user, nil) end end context "when an order_cycle_id is provided in params" do before { spree_get :for_order_cycle, format: :json, order_cycle_id: 1 } - it "calls order_cycle_enterprises_for() with the existing OrderCycle" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("existing OrderCycle") + it "initializes permissions with the existing OrderCycle" do + expect(OpenFoodNetwork::OrderCyclePermissions).to have_received(:new).with(user, "existing OrderCycle") end end context "when a coordinator is provided in params" do before { spree_get :for_order_cycle, format: :json, coordinator_id: 1 } - it "calls order_cycle_enterprises_for() with a new OrderCycle" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("new OrderCycle") + it "initializes permissions with a new OrderCycle" do + expect(OpenFoodNetwork::OrderCyclePermissions).to have_received(:new).with(user, "new OrderCycle") end end context "when both an order cycle and a coordinator are provided in params" do before { spree_get :for_order_cycle, format: :json, order_cycle_id: 1, coordinator_id: 1 } - it "calls order_cycle_enterprises_for() with the existing OrderCycle" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("existing OrderCycle") + it "initializes permissions with the existing OrderCycle" do + expect(OpenFoodNetwork::OrderCyclePermissions).to have_received(:new).with(user, "existing OrderCycle") end end end diff --git a/spec/lib/open_food_network/order_cycle_permissions_spec.rb b/spec/lib/open_food_network/order_cycle_permissions_spec.rb new file mode 100644 index 0000000000..b3279fc11a --- /dev/null +++ b/spec/lib/open_food_network/order_cycle_permissions_spec.rb @@ -0,0 +1,797 @@ +require 'open_food_network/order_cycle_permissions' + +module OpenFoodNetwork + describe OrderCyclePermissions do + let(:coordinator) { create(:distributor_enterprise) } + let(:hub) { create(:distributor_enterprise) } + let(:producer) { create(:supplier_enterprise) } + let(:user) { double(:user) } + let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } + let(:permissions) { OrderCyclePermissions.new(user, oc) } + + describe "finding enterprises that can be viewed in the order cycle interface" do + context "when permissions are initialized without an order_cycle" do + let(:permissions) { OrderCyclePermissions.new(user, nil) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns an empty scope" do + expect(permissions.order_cycle_enterprises_for).to be_empty + end + end + + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns the coordinator itself" do + expect(permissions.order_cycle_enterprises_for).to include coordinator + end + + context "where P-OC has been granted to the coordinator by other enterprises" do + before do + create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) + end + + context "where the coordinator sells any" do + it "returns enterprises which have granted P-OC to the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include hub + expect(enterprises).to_not include producer + end + end + + context "where the coordinator sells 'own'" do + before { coordinator.stub(:sells) { 'own' } } + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer + end + end + end + + context "where P-OC has not been granted to the coordinator by other enterprises" do + context "where the other enterprise are already in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + context "where the coordinator sells any" do + it "returns enterprises which have granted P-OC to the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include hub, producer + end + end + + context "where the coordinator sells 'own'" do + before { coordinator.stub(:sells) { 'own' } } + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer + end + end + end + + context "where the other enterprises are not in the order cycle" do + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer + end + end + end + end + + context "as a manager of a hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + end + + context "that has granted P-OC to the coordinator" do + before do + create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) + end + + context "where my hub is in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns my hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include hub + expect(enterprises).to_not include producer, coordinator + end + + context "and has been granted P-OC by a producer" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where the producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns the producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + end + end + + context "where the producer is not in the order cycle" do + # No incoming exchange + + it "does not return the producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include producer + end + end + end + + context "and has granted P-OC to a producer" do + before do + create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) + end + + context "where the producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns the producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + end + end + + context "where the producer is not in the order cycle" do + # No incoming exchange + + it "does not return the producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include producer + end + end + end + end + + context "where my hub is not in the order cycle" do + # No outgoing exchange for my hub + + it "does not return my hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer, coordinator + end + end + end + + context "that has not granted P-OC to the coordinator" do + it "does not return my hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer, coordinator + end + + context "but is already in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns my hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include hub + expect(enterprises).to_not include producer, coordinator + end + + context "and distributes variants distributed by an unmanaged and unpermitted producer" do + before { ex.variants << create(:variant, product: create(:product, supplier: producer)) } + + # TODO: update this when we are confident about P-OCs + it "returns that producer as well" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + end + end + end + + context "as a manager of a producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + end + + context "which has granted P-OC to the coordinator" do + before do + create(:enterprise_relationship, parent: producer, child: coordinator, permissions_list: [:add_to_order_cycle]) + end + + context "where my producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns my producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer + expect(enterprises).to_not include hub, coordinator + end + + context "and has been granted P-OC by a hub" do + before do + create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is also in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns the hub as well" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return the hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub + end + end + end + + context "and has granted P-OC to a hub" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is also in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns the hub as well" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return the hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub + end + end + end + end + + context "where my producer is not in the order cycle" do + # No incoming exchange for producer + + it "does not return my producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer, coordinator + end + end + end + + context "which has not granted P-OC to the coordinator" do + it "does not return my producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include producer + end + + context "but is already in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + # TODO: update this when we are confident about P-OCs + it "returns my producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer + expect(enterprises).to_not include hub, coordinator + end + + context "and has variants distributed by an outgoing hub" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + before { ex_outgoing.variants << create(:variant, product: create(:product, supplier: producer)) } + + # TODO: update this when we are confident about P-OCs + it "returns that hub as well" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + end + end + end + end + + describe "finding exchanges of an order cycle that an admin can manage" do + describe "as the manager of the coordinator" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns all exchanges in the order cycle, regardless of hubE permissions" do + permissions.order_cycle_exchanges.should include ex_in, ex_out + end + end + + + describe "as the manager of a hub" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + end + + context "where my hub is in the order cycle" do + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns my hub's outgoing exchange" do + permissions.order_cycle_exchanges.should == [ex_out] + end + + context "where my hub has been granted P-OC by an incoming producer" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns the producer's incoming exchange" do + permissions.order_cycle_exchanges.should include ex_in + end + end + + context "where my hub has not been granted P-OC by an incoming producer" do + it "returns the producers's incoming exchange, and my own outhoing exchange" do + permissions.order_cycle_exchanges.should_not include ex_in + end + end + end + + context "where my hub isn't in the order cycle" do + it "does not return the producer's incoming exchanges" do + permissions.order_cycle_exchanges.should == [] + end + end + + # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required + describe "legacy compatability" do + context "where my hub's outgoing exchange contains variants of a producer I don't manage and has not given my hub P-OC" do + let!(:product) { create(:product, supplier: producer) } + let!(:variant) { create(:variant, product: product) } + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: true) } + before { ex_out.variants << variant } + + it "returns incoming exchanges supplying the variants in my outgoing exchange" do + permissions.order_cycle_exchanges.should include ex_out + end + end + end + end + + describe "as the manager of a producer" do + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + end + + context "where my producer supplies to the order cycle" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns my producer's incoming exchange" do + permissions.order_cycle_exchanges.should == [ex_in] + end + + context "my producer has granted P-OC to an outgoing hub" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns the hub's outgoing exchange" do + permissions.order_cycle_exchanges.should include ex_out + end + end + + context "my producer has not granted P-OC to an outgoing hub" do + it "does not return the hub's outgoing exchange" do + permissions.order_cycle_exchanges.should_not include ex_out + end + end + end + + context "where my producer doesn't supply the order cycle" do + it "does not return the hub's outgoing exchanges" do + permissions.order_cycle_exchanges.should == [] + end + end + + # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required + describe "legacy compatability" do + context "where an outgoing exchange contains variants of a producer I manage" do + let!(:product) { create(:product, supplier: producer) } + let!(:variant) { create(:variant, product: product) } + before { ex_out.variants << variant } + + context "where my producer supplies to the order cycle" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns the outgoing exchange" do + permissions.order_cycle_exchanges.should include ex_out + end + end + + context "where my producer doesn't supply to the order cycle" do + it "does not return the outgoing exchange" do + permissions.order_cycle_exchanges.should_not include ex_out + end + end + end + end + end + end + + + describe "finding the variants within a hypothetical exchange between two enterprises which are visible to a user" do + let!(:producer1) { create(:supplier_enterprise) } + let!(:producer2) { create(:supplier_enterprise) } + let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } + let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + describe "incoming exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.visible_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.visible_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of a hub which has been granted P-OC by the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns variants produced by that producer only" do + visible = permissions.visible_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return variants produced by that producer" do + visible = permissions.visible_variants_for_incoming_exchanges_from(producer1) + expect(visible).to_not include v1, v2 + end + end + end + end + + describe "outgoing exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + context "where the coordinator produces products" do + let!(:v3) { create(:variant, product: create(:simple_product, supplier: coordinator)) } + + it "returns any variants produced by the coordinator itself for exchanges with 'self'" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(coordinator) + expect(visible).to include v3 + expect(visible).to_not include v1, v2 + end + + it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2, v3 + end + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to hub (or coordinator for that matter) + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1, v2 + end + end + end + + context "as manager of an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to hub + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1, v2 + end + end + end + + context "as the manager of a producer which has granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where my producer is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: coordinator, incoming: true) } + + it "returns all of my produced variants" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where my producer isn't in the order cycle" do + # No incoming exchange + + it "does not return my variants" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v2 + end + end + end + + context "as the manager of a producer which has not granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns an empty array" do + expect(permissions.visible_variants_for_outgoing_exchanges_to(hub)).to eq [] + end + + # TODO: for backwards compatability, remove later + context "but which has variants already in the exchange" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + # This one won't be in the exchange, and so shouldn't be visible + let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v3 + expect(visible).to include v2 + end + end + end + end + end + + describe "finding the variants within a hypothetical exchange between two enterprises which are editable by a user" do + let!(:producer1) { create(:supplier_enterprise) } + let!(:producer2) { create(:supplier_enterprise) } + let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } + let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + describe "incoming exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.editable_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.editable_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of a hub which has been granted P-OC by the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "does not return variants produced by that producer" do + visible = permissions.editable_variants_for_incoming_exchanges_from(producer1) + expect(visible).to_not include v1, v2 + end + end + end + + describe "outgoing exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + context "where the coordinator produces products" do + let!(:v3) { create(:variant, product: create(:simple_product, supplier: coordinator)) } + + it "returns any variants produced by the coordinator itself for exchanges with 'self'" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(coordinator) + expect(visible).to include v3 + expect(visible).to_not include v1, v2 + end + + it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2, v3 + end + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to hub (or coordinator for that matter) + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1, v2 + end + end + end + + context "as manager of an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to hub + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1, v2 + end + end + end + + context "as the manager of a producer which has granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where my producer is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: coordinator, incoming: true) } + + context "where the outgoing hub has granted P-OC to my producer" do + before do + create(:enterprise_relationship, parent: hub, child: producer1, permissions_list: [:add_to_order_cycle]) + end + + it "returns all of my produced variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where the outgoing hub has not granted P-OC to my producer" do + # No permission granted + + it "does not return my variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v2 + end + end + end + + context "where my producer isn't in the order cycle" do + # No incoming exchange + + it "does not return my variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v2 + end + end + end + + context "as the manager of a producer which has not granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns an empty array" do + expect(permissions.editable_variants_for_outgoing_exchanges_to(hub)).to eq [] + end + + # TODO: for backwards compatability, remove later + context "but which has variants already in the exchange" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + # This one won't be in the exchange, and so shouldn't be visible + let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + before { ex.variants << v2 } + + it "does not return my variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v2, v3 + end + end + end + end + end + end +end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index ddbdd5a359..88611b857c 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -21,302 +21,6 @@ module OpenFoodNetwork end end - describe "finding enterprises that can be viewed in the order cycle interface" do - let(:coordinator) { create(:distributor_enterprise) } - let(:hub) { create(:distributor_enterprise) } - let(:producer) { create(:supplier_enterprise) } - let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } - - context "when no order_cycle or coordinator are provided for reference" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } - end - - it "returns an empty scope" do - expect(permissions.order_cycle_enterprises_for(nil)).to be_empty - end - end - - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } - end - - it "returns the coordinator itself" do - expect(permissions.order_cycle_enterprises_for(oc)).to include coordinator - end - - context "where P-OC has been granted to the coordinator by other enterprises" do - before do - create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) - end - - context "where the coordinator sells any" do - it "returns enterprises which have granted P-OC to the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include hub - expect(enterprises).to_not include producer - end - end - - context "where the coordinator sells 'own'" do - before { coordinator.stub(:sells) { 'own' } } - it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer - end - end - end - - context "where P-OC has not been granted to the coordinator by other enterprises" do - context "where the other enterprise are already in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - context "where the coordinator sells any" do - it "returns enterprises which have granted P-OC to the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include hub, producer - end - end - - context "where the coordinator sells 'own'" do - before { coordinator.stub(:sells) { 'own' } } - it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer - end - end - end - - context "where the other enterprises are not in the order cycle" do - it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer - end - end - end - end - - context "as a manager of a hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } - end - - context "that has granted P-OC to the coordinator" do - before do - create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) - end - - context "where my hub is in the order cycle" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include hub - expect(enterprises).to_not include producer, coordinator - end - - context "and has been granted P-OC by a producer" do - before do - create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) - end - - context "where the producer is in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - - it "returns the producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - end - end - - context "where the producer is not in the order cycle" do - # No incoming exchange - - it "does not return the producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include producer - end - end - end - - context "and has granted P-OC to a producer" do - before do - create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) - end - - context "where the producer is in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - - it "returns the producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - end - end - - context "where the producer is not in the order cycle" do - # No incoming exchange - - it "does not return the producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include producer - end - end - end - end - - context "where my hub is not in the order cycle" do - # No outgoing exchange for my hub - - it "does not return my hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer, coordinator - end - end - end - - context "that has not granted P-OC to the coordinator" do - it "does not return my hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer, coordinator - end - - context "but is already in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include hub - expect(enterprises).to_not include producer, coordinator - end - - context "and distributes variants distributed by an unmanaged and unpermitted producer" do - before { ex.variants << create(:variant, product: create(:product, supplier: producer)) } - - # TODO: update this when we are confident about P-OCs - it "returns that producer as well" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator - end - end - end - end - end - - context "as a manager of a producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } - end - - context "which has granted P-OC to the coordinator" do - before do - create(:enterprise_relationship, parent: producer, child: coordinator, permissions_list: [:add_to_order_cycle]) - end - - context "where my producer is in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - - it "returns my producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer - expect(enterprises).to_not include hub, coordinator - end - - context "and has been granted P-OC by a hub" do - before do - create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) - end - - context "where the hub is also in the order cycle" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - it "returns the hub as well" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator - end - end - - context "where the hub is not in the order cycle" do - # No outgoing exchange - - it "does not return the hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub - end - end - end - - context "and has granted P-OC to a hub" do - before do - create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) - end - - context "where the hub is also in the order cycle" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - it "returns the hub as well" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator - end - end - - context "where the hub is not in the order cycle" do - # No outgoing exchange - - it "does not return the hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub - end - end - end - end - - context "where my producer is not in the order cycle" do - # No incoming exchange for producer - - it "does not return my producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer, coordinator - end - end - end - - context "which has not granted P-OC to the coordinator" do - it "does not return my producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include producer - end - - context "but is already in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - - # TODO: update this when we are confident about P-OCs - it "returns my producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer - expect(enterprises).to_not include hub, coordinator - end - - context "and has variants distributed by an outgoing hub" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - before { ex_outgoing.variants << create(:variant, product: create(:product, supplier: producer)) } - - # TODO: update this when we are confident about P-OCs - it "returns that hub as well" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator - end - end - end - end - end - end - describe "finding enterprises whose profiles can be edited" do let(:e) { double(:enterprise) } @@ -404,502 +108,6 @@ module OpenFoodNetwork end end - describe "finding exchanges of an order cycle that an admin can manage" do - let!(:producer) { create(:supplier_enterprise) } - let(:oc) { create(:simple_order_cycle) } - - describe "as the manager of the coordinator" do - let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } - let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - end - - it "returns all exchanges in the order cycle, regardless of E2E permissions" do - permissions.order_cycle_exchanges(oc).should include ex_in, ex_out - end - end - - - describe "as the manager of a hub" do - let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } - - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - end - - context "where my hub is in the order cycle" do - let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - it "returns my hub's outgoing exchange" do - permissions.order_cycle_exchanges(oc).should == [ex_out] - end - - context "where my hub has been granted P-OC by an incoming producer" do - before do - create(:enterprise_relationship, parent: producer, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns the producer's incoming exchange" do - permissions.order_cycle_exchanges(oc).should include ex_in - end - end - - context "where my hub has not been granted P-OC by an incoming producer" do - it "returns the producers's incoming exchange, and my own outhoing exchange" do - permissions.order_cycle_exchanges(oc).should_not include ex_in - end - end - end - - context "where my hub isn't in the order cycle" do - it "does not return the producer's incoming exchanges" do - permissions.order_cycle_exchanges(oc).should == [] - end - end - - # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required - describe "legacy compatability" do - context "where my hub's outgoing exchange contains variants of a producer I don't manage and has not given my hub P-OC" do - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { create(:variant, product: product) } - let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: true) } - before { ex_out.variants << variant } - - it "returns incoming exchanges supplying the variants in my outgoing exchange" do - permissions.order_cycle_exchanges(oc).should include ex_out - end - end - end - end - - describe "as the manager of a producer" do - let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } - end - - context "where my producer supplies to the order cycle" do - let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } - - it "returns my producer's incoming exchange" do - permissions.order_cycle_exchanges(oc).should == [ex_in] - end - - context "my producer has granted P-OC to an outgoing hub" do - before do - create(:enterprise_relationship, parent: producer, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns the hub's outgoing exchange" do - permissions.order_cycle_exchanges(oc).should include ex_out - end - end - - context "my producer has not granted P-OC to an outgoing hub" do - it "does not return the hub's outgoing exchange" do - permissions.order_cycle_exchanges(oc).should_not include ex_out - end - end - end - - context "where my producer doesn't supply the order cycle" do - it "does not return the hub's outgoing exchanges" do - permissions.order_cycle_exchanges(oc).should == [] - end - end - - # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required - describe "legacy compatability" do - context "where an outgoing exchange contains variants of a producer I manage" do - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { create(:variant, product: product) } - before { ex_out.variants << variant } - - context "where my producer supplies to the order cycle" do - let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } - - it "returns the outgoing exchange" do - permissions.order_cycle_exchanges(oc).should include ex_out - end - end - - context "where my producer doesn't supply to the order cycle" do - it "does not return the outgoing exchange" do - permissions.order_cycle_exchanges(oc).should_not include ex_out - end - end - end - end - end - end - - describe "finding the variants within a hypothetical exchange between two enterprises which are visible to a user" do - let!(:producer1) { create(:supplier_enterprise) } - let!(:producer2) { create(:supplier_enterprise) } - let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } - let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } - let(:oc) { create(:simple_order_cycle) } - - describe "incoming exchanges" do - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - end - - it "returns all variants belonging to the sending producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "as a manager of the producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } - end - - it "returns all variants belonging to the sending producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "as a manager of a hub which has been granted P-OC by the producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - context "where the hub is in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - it "returns variants produced by that producer only" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "where the hub is not in the order cycle" do - # No outgoing exchange - - it "does not return variants produced by that producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - end - - describe "outgoing exchanges" do - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - - context "where the coordinator produces products" do - let!(:v3) { create(:variant, product: create(:simple_product, supplier: e1)) } - - it "returns any variants produced by the coordinator itself for exchanges with 'self'" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e1, order_cycle: oc) - expect(visible).to include v3 - expect(visible).to_not include v1, v2 - end - - it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2, v3 - end - end - - # TODO: for backwards compatability, remove later - context "when an exchange exists between the coordinator and the hub within this order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - # producer2 produces v2 and has not granted P-OC to e2 (or e1 for that matter) - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1, v2 - end - end - end - - context "as manager of an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - - # TODO: for backwards compatability, remove later - context "when an exchange exists between the coordinator and the hub within this order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - # producer2 produces v2 and has not granted P-OC to e2 - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1, v2 - end - end - end - - context "as the manager of a producer which has granted P-OC to an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - context "where my producer is in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: e1, incoming: true) } - - it "returns all of my produced variants" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "where my producer isn't in the order cycle" do - # No incoming exchange - - it "does not return my variants" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - - context "as the manager of a producer which has not granted P-OC to an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns an empty array" do - expect(permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc)).to eq [] - end - - # TODO: for backwards compatability, remove later - context "but which has variants already in the exchange" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - # This one won't be in the exchange, and so shouldn't be visible - let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } - - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v3 - expect(visible).to include v2 - end - end - end - end - end - - describe "finding the variants within a hypothetical exchange between two enterprises which are editable by a user" do - let!(:producer1) { create(:supplier_enterprise) } - let!(:producer2) { create(:supplier_enterprise) } - let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } - let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } - let(:oc) { create(:simple_order_cycle) } - - describe "incoming exchanges" do - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - end - - it "returns all variants belonging to the sending producer" do - visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "as a manager of the producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } - end - - it "returns all variants belonging to the sending producer" do - visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "as a manager of a hub which has been granted P-OC by the producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "does not return variants produced by that producer" do - visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - - describe "outgoing exchanges" do - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - - context "where the coordinator produces products" do - let!(:v3) { create(:variant, product: create(:simple_product, supplier: e1)) } - - it "returns any variants produced by the coordinator itself for exchanges with 'self'" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e1, order_cycle: oc) - expect(visible).to include v3 - expect(visible).to_not include v1, v2 - end - - it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2, v3 - end - end - - # TODO: for backwards compatability, remove later - context "when an exchange exists between the coordinator and the hub within this order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - # producer2 produces v2 and has not granted P-OC to e2 (or e1 for that matter) - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1, v2 - end - end - end - - context "as manager of an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - - # TODO: for backwards compatability, remove later - context "when an exchange exists between the coordinator and the hub within this order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - # producer2 produces v2 and has not granted P-OC to e2 - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1, v2 - end - end - end - - context "as the manager of a producer which has granted P-OC to an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - context "where my producer is in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: e1, incoming: true) } - - context "where the outgoing hub has granted P-OC to my producer" do - before do - create(:enterprise_relationship, parent: e2, child: producer1, permissions_list: [:add_to_order_cycle]) - end - - it "returns all of my produced variants" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "where the outgoing hub has not granted P-OC to my producer" do - # No permission granted - - it "does not return my variants" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - - context "where my producer isn't in the order cycle" do - # No incoming exchange - - it "does not return my variants" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - - context "as the manager of a producer which has not granted P-OC to an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns an empty array" do - expect(permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc)).to eq [] - end - - # TODO: for backwards compatability, remove later - context "but which has variants already in the exchange" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - # This one won't be in the exchange, and so shouldn't be visible - let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } - - before { ex.variants << v2 } - - it "does not return my variants" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v2, v3 - end - end - end - end - end - describe "finding managed products" do let!(:p1) { create(:simple_product) } let!(:p2) { create(:simple_product) } diff --git a/spec/serializers/admin/exchange_serializer_spec.rb b/spec/serializers/admin/exchange_serializer_spec.rb index 9b32c537b3..b45f6bcb9b 100644 --- a/spec/serializers/admin/exchange_serializer_spec.rb +++ b/spec/serializers/admin/exchange_serializer_spec.rb @@ -1,3 +1,5 @@ +require 'open_food_network/order_cycle_permissions' + describe Api::Admin::ExchangeSerializer do let(:v1) { create(:variant) } let(:v2) { create(:variant) } @@ -7,8 +9,8 @@ describe Api::Admin::ExchangeSerializer do before do - allow(OpenFoodNetwork::Permissions).to receive(:new) { permissions_mock } - allow(permissions_mock).to receive(:visible_variants_for_outgoing_exchanges_between) do + allow(OpenFoodNetwork::OrderCyclePermissions).to receive(:new) { permissions_mock } + allow(permissions_mock).to receive(:visible_variants_for_outgoing_exchanges_to) do # This is the permitted list of variants Spree::Variant.where(id: [v1] ) end @@ -16,8 +18,7 @@ describe Api::Admin::ExchangeSerializer do it "filters variants within the exchange based on permissions" do visible_variants = serializer.variants - expect(permissions_mock).to have_received(:visible_variants_for_outgoing_exchanges_between). - with(exchange.sender, exchange.receiver, order_cycle: exchange.order_cycle) + expect(permissions_mock).to have_received(:visible_variants_for_outgoing_exchanges_to).with(exchange.receiver) expect(exchange.variants).to include v1, v2 expect(visible_variants.keys).to include v1.id expect(visible_variants.keys).to_not include v2.id From e3138c3cd2878b0ea7f6114679a8678896f6c4b3 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 22:25:23 +1000 Subject: [PATCH 348/384] Renaming order_cycle_enterprises_for as visible_enterprises --- .../admin/enterprise_fees_controller.rb | 2 +- .../admin/enterprises_controller.rb | 2 +- app/helpers/order_cycles_helper.rb | 2 +- .../api/admin/order_cycle_serializer.rb | 6 +-- app/views/admin/order_cycles/_row.html.haml | 4 +- .../order_cycle_form_applicator.rb | 2 +- .../order_cycle_permissions.rb | 2 +- .../admin/enterprises_controller_spec.rb | 2 +- .../order_cycle_permissions_spec.rb | 50 +++++++++---------- 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index f665bc1c37..b8ea46689f 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -79,7 +79,7 @@ module Admin order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id] coordinator = Enterprise.find_by_id(params[:coordinator_id]) if params[:coordinator_id] order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present? - enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for + enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises return EnterpriseFee.for_enterprises(enterprises).order('enterprise_id', 'fee_type', 'name') else collection = EnterpriseFee.managed_by(spree_current_user).order('enterprise_id', 'fee_type', 'name') diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 9776f23e69..408fa63e02 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -108,7 +108,7 @@ module Admin order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id] coordinator = Enterprise.find_by_id(params[:coordinator_id]) if params[:coordinator_id] order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present? - return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for + return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises else # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now OpenFoodNetwork::Permissions.new(spree_current_user). diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index acbe28720f..18222ee1f0 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -4,7 +4,7 @@ module OrderCyclesHelper end def permitted_enterprises_for(order_cycle) - OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for + OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises end def permitted_producer_enterprises_for(order_cycle) diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 795daf85bc..27eeab7c07 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -28,7 +28,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # work out which variants should be editable within incoming exchanges from that enterprise editable = {} permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises enterprises.each do |enterprise| variants = permissions.editable_variants_for_incoming_exchanges_from(enterprise).pluck(:id) editable[enterprise.id] = variants if variants.any? @@ -41,7 +41,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # work out which variants should be editable within incoming exchanges from that enterprise editable = {} permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises enterprises.each do |enterprise| variants = permissions.editable_variants_for_outgoing_exchanges_to(enterprise).pluck(:id) editable[enterprise.id] = variants if variants.any? @@ -54,7 +54,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # work out which variants should be visible within outgoing exchanges from that enterprise visible = {} permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises enterprises.each do |enterprise| variants = permissions.visible_variants_for_outgoing_exchanges_to(enterprise).pluck(:id) visible[enterprise.id] = variants if variants.any? diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 1af364440e..2594d5c9f3 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -8,7 +8,7 @@ - unless order_cycles_simple_index %td.suppliers - - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for) + - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises) - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 %span.with-tip{'data-powertip' => supplier_list} @@ -18,7 +18,7 @@ = supplier_list %td= order_cycle.coordinator.name %td.distributors - - distributors = order_cycle.distributors.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for) + - distributors = order_cycle.distributors.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises) - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 %span.with-tip{'data-powertip' => distributor_list} diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index 81e070049c..69ba284cd8 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -103,7 +103,7 @@ module OpenFoodNetwork def permitted_enterprises return @permitted_enterprises unless @permitted_enterprises.nil? @permitted_enterprises = OpenFoodNetwork::OrderCyclePermissions. - new(@spree_current_user, @order_cycle).order_cycle_enterprises_for + new(@spree_current_user, @order_cycle).visible_enterprises end def manages_coordinator? diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index 80c7bb9f19..8d08911796 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -11,7 +11,7 @@ module OpenFoodNetwork # List of any enterprises whose exchanges I should be able to see in order_cycle # NOTE: the enterprises a given user can see actually in the OC interface depend on the relationships # of their enterprises to the coordinator of the order cycle, rather than on the order cycle itself - def order_cycle_enterprises_for + def visible_enterprises return Enterprise.where("1=0") unless @coordinator.present? if managed_enterprises.include? @coordinator coordinator_permitted = [@coordinator] diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 1b5849ef9f..4604520c00 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -420,7 +420,7 @@ module Admin OrderCycle.stub new: "new OrderCycle" allow(OpenFoodNetwork::OrderCyclePermissions).to receive(:new) { permission_mock } - allow(permission_mock).to receive(:order_cycle_enterprises_for) { [] } + allow(permission_mock).to receive(:visible_enterprises) { [] } allow(ActiveModel::ArraySerializer).to receive(:new) { "" } end diff --git a/spec/lib/open_food_network/order_cycle_permissions_spec.rb b/spec/lib/open_food_network/order_cycle_permissions_spec.rb index b3279fc11a..9423f7014a 100644 --- a/spec/lib/open_food_network/order_cycle_permissions_spec.rb +++ b/spec/lib/open_food_network/order_cycle_permissions_spec.rb @@ -18,7 +18,7 @@ module OpenFoodNetwork end it "returns an empty scope" do - expect(permissions.order_cycle_enterprises_for).to be_empty + expect(permissions.visible_enterprises).to be_empty end end @@ -28,7 +28,7 @@ module OpenFoodNetwork end it "returns the coordinator itself" do - expect(permissions.order_cycle_enterprises_for).to include coordinator + expect(permissions.visible_enterprises).to include coordinator end context "where P-OC has been granted to the coordinator by other enterprises" do @@ -38,7 +38,7 @@ module OpenFoodNetwork context "where the coordinator sells any" do it "returns enterprises which have granted P-OC to the coordinator" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include hub expect(enterprises).to_not include producer end @@ -47,7 +47,7 @@ module OpenFoodNetwork context "where the coordinator sells 'own'" do before { coordinator.stub(:sells) { 'own' } } it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include hub, producer end end @@ -60,7 +60,7 @@ module OpenFoodNetwork context "where the coordinator sells any" do it "returns enterprises which have granted P-OC to the coordinator" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include hub, producer end end @@ -68,7 +68,7 @@ module OpenFoodNetwork context "where the coordinator sells 'own'" do before { coordinator.stub(:sells) { 'own' } } it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include hub, producer end end @@ -76,7 +76,7 @@ module OpenFoodNetwork context "where the other enterprises are not in the order cycle" do it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include hub, producer end end @@ -97,7 +97,7 @@ module OpenFoodNetwork let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include hub expect(enterprises).to_not include producer, coordinator end @@ -111,7 +111,7 @@ module OpenFoodNetwork let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } it "returns the producer" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include producer, hub end end @@ -120,7 +120,7 @@ module OpenFoodNetwork # No incoming exchange it "does not return the producer" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include producer end end @@ -135,7 +135,7 @@ module OpenFoodNetwork let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } it "returns the producer" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include producer, hub end end @@ -144,7 +144,7 @@ module OpenFoodNetwork # No incoming exchange it "does not return the producer" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include producer end end @@ -155,7 +155,7 @@ module OpenFoodNetwork # No outgoing exchange for my hub it "does not return my hub" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include hub, producer, coordinator end end @@ -163,7 +163,7 @@ module OpenFoodNetwork context "that has not granted P-OC to the coordinator" do it "does not return my hub" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include hub, producer, coordinator end @@ -171,7 +171,7 @@ module OpenFoodNetwork let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include hub expect(enterprises).to_not include producer, coordinator end @@ -181,7 +181,7 @@ module OpenFoodNetwork # TODO: update this when we are confident about P-OCs it "returns that producer as well" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end @@ -204,7 +204,7 @@ module OpenFoodNetwork let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } it "returns my producer" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include producer expect(enterprises).to_not include hub, coordinator end @@ -218,7 +218,7 @@ module OpenFoodNetwork let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns the hub as well" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end @@ -228,7 +228,7 @@ module OpenFoodNetwork # No outgoing exchange it "does not return the hub" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include hub end end @@ -243,7 +243,7 @@ module OpenFoodNetwork let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns the hub as well" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end @@ -253,7 +253,7 @@ module OpenFoodNetwork # No outgoing exchange it "does not return the hub" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include hub end end @@ -264,7 +264,7 @@ module OpenFoodNetwork # No incoming exchange for producer it "does not return my producer" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include hub, producer, coordinator end end @@ -272,7 +272,7 @@ module OpenFoodNetwork context "which has not granted P-OC to the coordinator" do it "does not return my producer" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to_not include producer end @@ -281,7 +281,7 @@ module OpenFoodNetwork # TODO: update this when we are confident about P-OCs it "returns my producer" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include producer expect(enterprises).to_not include hub, coordinator end @@ -292,7 +292,7 @@ module OpenFoodNetwork # TODO: update this when we are confident about P-OCs it "returns that hub as well" do - enterprises = permissions.order_cycle_enterprises_for + enterprises = permissions.visible_enterprises expect(enterprises).to include producer, hub expect(enterprises).to_not include coordinator end From 6ac45f3dc835c9dd3cad51935f69ce5c534791ff Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 22:26:55 +1000 Subject: [PATCH 349/384] Renaming order_cycle_exchanges as visible_exchanges --- .../api/admin/order_cycle_serializer.rb | 2 +- .../order_cycle_permissions.rb | 2 +- .../order_cycle_permissions_spec.rb | 24 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 27eeab7c07..d305612790 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -19,7 +19,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer end def exchanges - scoped_exchanges = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).order_cycle_exchanges.order('id ASC') + scoped_exchanges = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).visible_exchanges.order('id ASC') ActiveModel::ArraySerializer.new(scoped_exchanges, {each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user] }) end diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index 8d08911796..d63bcd3943 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -72,7 +72,7 @@ module OpenFoodNetwork end # Find the exchanges of an order cycle that an admin can manage - def order_cycle_exchanges + def visible_exchanges ids = order_cycle_exchange_ids_involving_my_enterprises | order_cycle_exchange_ids_distributing_my_variants | order_cycle_exchange_ids_with_distributable_variants diff --git a/spec/lib/open_food_network/order_cycle_permissions_spec.rb b/spec/lib/open_food_network/order_cycle_permissions_spec.rb index 9423f7014a..d135a3500a 100644 --- a/spec/lib/open_food_network/order_cycle_permissions_spec.rb +++ b/spec/lib/open_food_network/order_cycle_permissions_spec.rb @@ -312,7 +312,7 @@ module OpenFoodNetwork end it "returns all exchanges in the order cycle, regardless of hubE permissions" do - permissions.order_cycle_exchanges.should include ex_in, ex_out + permissions.visible_exchanges.should include ex_in, ex_out end end @@ -328,7 +328,7 @@ module OpenFoodNetwork let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } it "returns my hub's outgoing exchange" do - permissions.order_cycle_exchanges.should == [ex_out] + permissions.visible_exchanges.should == [ex_out] end context "where my hub has been granted P-OC by an incoming producer" do @@ -337,20 +337,20 @@ module OpenFoodNetwork end it "returns the producer's incoming exchange" do - permissions.order_cycle_exchanges.should include ex_in + permissions.visible_exchanges.should include ex_in end end context "where my hub has not been granted P-OC by an incoming producer" do it "returns the producers's incoming exchange, and my own outhoing exchange" do - permissions.order_cycle_exchanges.should_not include ex_in + permissions.visible_exchanges.should_not include ex_in end end end context "where my hub isn't in the order cycle" do it "does not return the producer's incoming exchanges" do - permissions.order_cycle_exchanges.should == [] + permissions.visible_exchanges.should == [] end end @@ -363,7 +363,7 @@ module OpenFoodNetwork before { ex_out.variants << variant } it "returns incoming exchanges supplying the variants in my outgoing exchange" do - permissions.order_cycle_exchanges.should include ex_out + permissions.visible_exchanges.should include ex_out end end end @@ -380,7 +380,7 @@ module OpenFoodNetwork let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } it "returns my producer's incoming exchange" do - permissions.order_cycle_exchanges.should == [ex_in] + permissions.visible_exchanges.should == [ex_in] end context "my producer has granted P-OC to an outgoing hub" do @@ -389,20 +389,20 @@ module OpenFoodNetwork end it "returns the hub's outgoing exchange" do - permissions.order_cycle_exchanges.should include ex_out + permissions.visible_exchanges.should include ex_out end end context "my producer has not granted P-OC to an outgoing hub" do it "does not return the hub's outgoing exchange" do - permissions.order_cycle_exchanges.should_not include ex_out + permissions.visible_exchanges.should_not include ex_out end end end context "where my producer doesn't supply the order cycle" do it "does not return the hub's outgoing exchanges" do - permissions.order_cycle_exchanges.should == [] + permissions.visible_exchanges.should == [] end end @@ -417,13 +417,13 @@ module OpenFoodNetwork let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } it "returns the outgoing exchange" do - permissions.order_cycle_exchanges.should include ex_out + permissions.visible_exchanges.should include ex_out end end context "where my producer doesn't supply to the order cycle" do it "does not return the outgoing exchange" do - permissions.order_cycle_exchanges.should_not include ex_out + permissions.visible_exchanges.should_not include ex_out end end end From 59a2c8485762f1c70ba8c81837a2120d2bec50d0 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 22:30:21 +1000 Subject: [PATCH 350/384] Rename managed_enterprises_in as managed_participating_enterprises --- lib/open_food_network/order_cycle_permissions.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index d63bcd3943..d54b67ce38 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -30,7 +30,7 @@ module OpenFoodNetwork Enterprise.where(id: coordinator_permitted | all_active) else # Any enterprises that I manage directly, which have granted P-OC to the coordinator - managed_permitted = granting(:add_to_order_cycle, to: [@coordinator], scope: managed_enterprises_in(@order_cycle) ).pluck(:id) + managed_permitted = granting(:add_to_order_cycle, to: [@coordinator], scope: managed_participating_enterprises ).pluck(:id) # Any hubs in this OC that have been granted P-OC by producers I manage in this OC hubs_permitted = granted(:add_to_order_cycle, by: managed_producers_in(@order_cycle), scope: @order_cycle.distributors).pluck(:id) @@ -50,7 +50,7 @@ module OpenFoodNetwork if @order_cycle # TODO: Remove this when all P-OC are sorted out # Any enterprises that I manage that are already in the order_cycle - managed_active = managed_enterprises_in(@order_cycle).pluck(:id) + managed_active = managed_participating_enterprises.pluck(:id) # TODO: Remove this when all P-OC are sorted out # Any hubs that currently have outgoing exchanges distributing variants of producers I manage @@ -184,16 +184,16 @@ module OpenFoodNetwork private - def managed_enterprises_in(order_cycle) + def managed_participating_enterprises managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors) end def managed_hubs_in(order_cycle) - managed_enterprises_in(order_cycle).is_hub + managed_participating_enterprises.is_hub end def managed_producers_in(order_cycle) - managed_enterprises_in(order_cycle).is_primary_producer + managed_participating_enterprises.is_primary_producer end def order_cycle_exchange_ids_involving_my_enterprises From 9c376f0b00b7e93c4564abf2713f83fdb6b3b409 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 22:32:15 +1000 Subject: [PATCH 351/384] Rename managed_hubs_in as managed_participating_hubs --- lib/open_food_network/order_cycle_permissions.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index d54b67ce38..f149cabaa3 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -39,10 +39,10 @@ module OpenFoodNetwork hubs_permitting = granting(:add_to_order_cycle, to: managed_producers_in(@order_cycle), scope: @order_cycle.distributors).pluck(:id) # Any producers in this OC that have been granted P-OC by hubs I manage in this OC - producers_permitted = granted(:add_to_order_cycle, by: managed_hubs_in(@order_cycle), scope: @order_cycle.suppliers).pluck(:id) + producers_permitted = granted(:add_to_order_cycle, by: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) # Any producers in this OC that have granted P-OC to hubs I manage in this OC - producers_permitting = granting(:add_to_order_cycle, to: managed_hubs_in(@order_cycle), scope: @order_cycle.suppliers).pluck(:id) + producers_permitting = granting(:add_to_order_cycle, to: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) managed_active = [] hubs_active = [] @@ -60,7 +60,7 @@ module OpenFoodNetwork # TODO: Remove this when all P-OC are sorted out # Any producers of variants that hubs I manage are currently distributing in this OC - variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", managed_hubs_in(@order_cycle), @order_cycle).pluck(:id).uniq + variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", managed_participating_hubs, @order_cycle).pluck(:id).uniq products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq producers_active = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq end @@ -88,7 +88,7 @@ module OpenFoodNetwork Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) else # All variants of the producer if it has granted P-OC to any of my managed hubs that are in this order cycle - permitted = EnterpriseRelationship.permitting(managed_hubs_in(@order_cycle)). + permitted = EnterpriseRelationship.permitting(managed_participating_hubs). permitted_by(producer).with_permission(:add_to_order_cycle).present? if permitted Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) @@ -188,7 +188,7 @@ module OpenFoodNetwork managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors) end - def managed_hubs_in(order_cycle) + def managed_participating_hubs managed_participating_enterprises.is_hub end @@ -203,7 +203,7 @@ module OpenFoodNetwork def order_cycle_exchange_ids_with_distributable_variants # Find my managed hubs in this order cycle - hubs = managed_hubs_in(@order_cycle) + hubs = managed_participating_hubs # Any incoming exchange where the producer has granted P-OC to one or more of those hubs producers = granting(:add_to_order_cycle, to: hubs, scope: Enterprise.is_primary_producer).pluck :id permitted_exchanges = @order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id From 22a37cdc8a00dc7e874a45606fd1bfb08aa0c025 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 22:44:32 +1000 Subject: [PATCH 352/384] Rename managed_producers_in as managed_participating_producers --- lib/open_food_network/order_cycle_permissions.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index f149cabaa3..445b04a734 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -33,10 +33,10 @@ module OpenFoodNetwork managed_permitted = granting(:add_to_order_cycle, to: [@coordinator], scope: managed_participating_enterprises ).pluck(:id) # Any hubs in this OC that have been granted P-OC by producers I manage in this OC - hubs_permitted = granted(:add_to_order_cycle, by: managed_producers_in(@order_cycle), scope: @order_cycle.distributors).pluck(:id) + hubs_permitted = granted(:add_to_order_cycle, by: managed_participating_producers, scope: @order_cycle.distributors).pluck(:id) # Any hubs in this OC that have granted P-OC to producers I manage in this OC - hubs_permitting = granting(:add_to_order_cycle, to: managed_producers_in(@order_cycle), scope: @order_cycle.distributors).pluck(:id) + hubs_permitting = granting(:add_to_order_cycle, to: managed_participating_producers, scope: @order_cycle.distributors).pluck(:id) # Any producers in this OC that have been granted P-OC by hubs I manage in this OC producers_permitted = granted(:add_to_order_cycle, by: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) @@ -135,7 +135,7 @@ module OpenFoodNetwork Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) else # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub - producers = granting(:add_to_order_cycle, to: [hub], scope: managed_producers_in(@order_cycle)) + producers = granting(:add_to_order_cycle, to: [hub], scope: managed_participating_producers) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break @@ -171,7 +171,7 @@ module OpenFoodNetwork Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) else # Any of my managed producers in this order cycle granted P-OC by the hub - granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_producers_in(@order_cycle)) + granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_participating_producers) # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub granting_producers = granting(:add_to_order_cycle, to: [hub], scope: granted_producers) @@ -185,14 +185,14 @@ module OpenFoodNetwork private def managed_participating_enterprises - managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors) + managed_enterprises.where(id: @order_cycle.suppliers | @order_cycle.distributors) end def managed_participating_hubs managed_participating_enterprises.is_hub end - def managed_producers_in(order_cycle) + def managed_participating_producers managed_participating_enterprises.is_primary_producer end @@ -222,7 +222,7 @@ module OpenFoodNetwork def order_cycle_exchange_ids_distributing_my_variants # Find my producers in this order cycle - producers = managed_producers_in(@order_cycle).pluck :id + producers = managed_participating_producers.pluck :id # Any outgoing exchange where the distributor has been granted P-OC by one or more of those producers hubs = granted(:add_to_order_cycle, by: producers, scope: Enterprise.is_hub) permitted_exchanges = @order_cycle.exchanges.outgoing.where(receiver_id: hubs).pluck :id From 6f81a9ba1863aeadf6bfd1eac94e0d0e652b1a6d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 22:53:57 +1000 Subject: [PATCH 353/384] Save common permission method results as instance variables --- lib/open_food_network/order_cycle_permissions.rb | 9 ++++++--- lib/open_food_network/permissions.rb | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index 445b04a734..add1c466cf 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -185,15 +185,18 @@ module OpenFoodNetwork private def managed_participating_enterprises - managed_enterprises.where(id: @order_cycle.suppliers | @order_cycle.distributors) + return @managed_participating_enterprises unless @managed_participating_enterprises.nil? + @managed_participating_enterprises = managed_enterprises.where(id: @order_cycle.suppliers | @order_cycle.distributors) end def managed_participating_hubs - managed_participating_enterprises.is_hub + return @managed_participating_hubs unless @managed_participating_hubs.nil? + @managed_participating_hubs = managed_participating_enterprises.is_hub end def managed_participating_producers - managed_participating_enterprises.is_primary_producer + return @managed_participating_producers unless @managed_participating_producers.nil? + @managed_participating_producers = managed_participating_enterprises.is_primary_producer end def order_cycle_exchange_ids_involving_my_enterprises diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 16319ced97..375873bc44 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -81,7 +81,8 @@ module OpenFoodNetwork end def managed_enterprises - Enterprise.managed_by(@user) + return @managed_enterprises unless managed_enterprises.nil? + @managed_enterprises = Enterprise.managed_by(@user) end def related_enterprises_with(permission) From 88bae329711ad0954c9f41adff974adf2da97d2b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 23:00:34 +1000 Subject: [PATCH 354/384] Adding method: user_manages_coordinator_or(enterprise) to make code a bit more terse --- lib/open_food_network/order_cycle_permissions.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index add1c466cf..2af60c5696 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -83,7 +83,8 @@ module OpenFoodNetwork # Find the variants that a user can POTENTIALLY see within incoming exchanges def visible_variants_for_incoming_exchanges_from(producer) return Spree::Variant.where("1=0") unless @order_cycle - if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + + if user_manages_coordinator_or(producer) # All variants belonging to the producer Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) else @@ -101,7 +102,8 @@ module OpenFoodNetwork # Find the variants that a user can edit within incoming exchanges def editable_variants_for_incoming_exchanges_from(producer) return Spree::Variant.where("1=0") unless @order_cycle - if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + + if user_manages_coordinator_or(producer) # All variants belonging to the producer Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) else @@ -114,7 +116,8 @@ module OpenFoodNetwork # as this requires first that the variant is included in an incoming exchange def visible_variants_for_outgoing_exchanges_to(hub) return Spree::Variant.where("1=0") unless @order_cycle - if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + + if user_manages_coordinator_or(hub) # Any variants produced by the coordinator, for outgoing exchanges with itself coordinator_variants = [] if hub == @coordinator @@ -150,7 +153,8 @@ module OpenFoodNetwork # Find the variants that a user is permitted edit within outgoing exchanges def editable_variants_for_outgoing_exchanges_to(hub) return Spree::Variant.where("1=0") unless @order_cycle - if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + + if user_manages_coordinator_or(hub) # Any variants produced by the coordinator, for outgoing exchanges with itself coordinator_variants = [] if hub == @coordinator @@ -184,6 +188,10 @@ module OpenFoodNetwork private + def user_manages_coordinator_or(enterprise) + managed_enterprises.pluck(:id).include?(@coordinator.id) || managed_enterprises.pluck(:id).include?(enterprise.id) + end + def managed_participating_enterprises return @managed_participating_enterprises unless @managed_participating_enterprises.nil? @managed_participating_enterprises = managed_enterprises.where(id: @order_cycle.suppliers | @order_cycle.distributors) From bc89018ee10965a674e111b8bc110bb54314c553 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 23:24:50 +1000 Subject: [PATCH 355/384] Refer to the right variable doofus --- lib/open_food_network/permissions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 375873bc44..4edb3d5f39 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -81,7 +81,7 @@ module OpenFoodNetwork end def managed_enterprises - return @managed_enterprises unless managed_enterprises.nil? + return @managed_enterprises unless @managed_enterprises.nil? @managed_enterprises = Enterprise.managed_by(@user) end From b24cad7aebbabf2b9bbbd42d155a4a00963013c1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 11 Apr 2015 23:25:30 +1000 Subject: [PATCH 356/384] Providing access to order_cycle_permissions in order cycles controller --- app/controllers/admin/order_cycles_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 6c9891baec..fe1157a7f3 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -1,4 +1,4 @@ -require 'open_food_network/permissions' +require 'open_food_network/order_cycle_permissions' require 'open_food_network/order_cycle_form_applicator' module Admin From 0f9fbe6e8ce6ae58bf288d7c2ae6419082b211e4 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 12 Apr 2015 10:53:22 +1000 Subject: [PATCH 357/384] Preventing relationships from being created with self --- lib/tasks/data.rake | 66 ++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/lib/tasks/data.rake b/lib/tasks/data.rake index 45985e6c50..90a23cd25a 100644 --- a/lib/tasks/data.rake +++ b/lib/tasks/data.rake @@ -6,47 +6,53 @@ namespace :openfoodnetwork do OrderCycle.where('updated_at > ?', Date.today - 3.months).each do |order_cycle| # Cycle through the incoming exchanges order_cycle.exchanges.incoming.each do |exchange| - # Ensure that an enterprise relationship from the producer to the coordinator exists - relationship = EnterpriseRelationship.where(parent_id: exchange.sender_id, child_id: exchange.receiver_id).first - unless relationship.present? - puts "CREATING: #{exchange.sender.name} TO #{exchange.receiver.name}" - relationship = EnterpriseRelationship.create!(parent_id: exchange.sender_id, child_id: exchange.receiver_id) - end - # And that P-OC is granted - unless relationship.has_permission?(:add_to_order_cycle) - puts "PERMITTING: #{exchange.sender.name} TO #{exchange.receiver.name}" - relationship.permissions.create!(name: :add_to_order_cycle) + unless exchange.sender == exchange.receiver + # Ensure that an enterprise relationship from the producer to the coordinator exists + relationship = EnterpriseRelationship.where(parent_id: exchange.sender_id, child_id: exchange.receiver_id).first + unless relationship.present? + puts "CREATING: #{exchange.sender.name} TO #{exchange.receiver.name}" + relationship = EnterpriseRelationship.create!(parent_id: exchange.sender_id, child_id: exchange.receiver_id) + end + # And that P-OC is granted + unless relationship.has_permission?(:add_to_order_cycle) + puts "PERMITTING: #{exchange.sender.name} TO #{exchange.receiver.name}" + relationship.permissions.create!(name: :add_to_order_cycle) + end end end # Cycle through the outgoing exchanges order_cycle.exchanges.outgoing.each do |exchange| - # Enure that an enterprise relationship from the hub to the coordinator exists - relationship = EnterpriseRelationship.where(parent_id: exchange.receiver_id, child_id: exchange.sender_id).first - unless relationship.present? - puts "CREATING: #{exchange.receiver.name} TO #{exchange.sender.name}" - relationship = EnterpriseRelationship.create!(parent_id: exchange.receiver_id, child_id: exchange.sender_id) - end - # And that P-OC is granted - unless relationship.has_permission?(:add_to_order_cycle) - puts "PERMITTING: #{exchange.receiver.name} TO #{exchange.sender.name}" - relationship.permissions.create!(name: :add_to_order_cycle) + unless exchange.sender == exchange.receiver + # Enure that an enterprise relationship from the hub to the coordinator exists + relationship = EnterpriseRelationship.where(parent_id: exchange.receiver_id, child_id: exchange.sender_id).first + unless relationship.present? + puts "CREATING: #{exchange.receiver.name} TO #{exchange.sender.name}" + relationship = EnterpriseRelationship.create!(parent_id: exchange.receiver_id, child_id: exchange.sender_id) + end + # And that P-OC is granted + unless relationship.has_permission?(:add_to_order_cycle) + puts "PERMITTING: #{exchange.receiver.name} TO #{exchange.sender.name}" + relationship.permissions.create!(name: :add_to_order_cycle) + end end # For each variant in the exchange products = Spree::Product.joins(:variants_including_master).where('spree_variants.id IN (?)', exchange.variants).pluck(:id).uniq producers = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).uniq producers.each do |producer| - # Ensure that an enterprise relationship from the producer to the hub exists - relationship = EnterpriseRelationship.where(parent_id: producer.id, child_id: exchange.receiver_id).first - unless relationship.present? - puts "CREATING: #{producer.name} TO #{exchange.receiver.name}" - relationship = EnterpriseRelationship.create!(parent_id: producer.id, child_id: exchange.receiver_id) - end - # And that P-OC is granted - unless relationship.has_permission?(:add_to_order_cycle) - puts "PERMITTING: #{producer.name} TO #{exchange.receiver.name}" - relationship.permissions.create!(name: :add_to_order_cycle) + unless producer == exchange.receiver + # Ensure that an enterprise relationship from the producer to the hub exists + relationship = EnterpriseRelationship.where(parent_id: producer.id, child_id: exchange.receiver_id).first + unless relationship.present? + puts "CREATING: #{producer.name} TO #{exchange.receiver.name}" + relationship = EnterpriseRelationship.create!(parent_id: producer.id, child_id: exchange.receiver_id) + end + # And that P-OC is granted + unless relationship.has_permission?(:add_to_order_cycle) + puts "PERMITTING: #{producer.name} TO #{exchange.receiver.name}" + relationship.permissions.create!(name: :add_to_order_cycle) + end end end end From 207274487079d8830001d5a922574225a094ef31 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 12 Apr 2015 11:38:18 +1000 Subject: [PATCH 358/384] User can enter the number of months back to search for order cycles from which to build relationships --- lib/tasks/data.rake | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/tasks/data.rake b/lib/tasks/data.rake index 90a23cd25a..23db3e6049 100644 --- a/lib/tasks/data.rake +++ b/lib/tasks/data.rake @@ -2,8 +2,10 @@ namespace :openfoodnetwork do namespace :data do desc "Adding relationships based on recent order cycles" task :create_order_cycle_relationships => :environment do + input = request_months + # For each order cycle which was modified within the past 3 months - OrderCycle.where('updated_at > ?', Date.today - 3.months).each do |order_cycle| + OrderCycle.where('updated_at > ?', Date.today - input.months).each do |order_cycle| # Cycle through the incoming exchanges order_cycle.exchanges.incoming.each do |exchange| unless exchange.sender == exchange.receiver @@ -58,5 +60,31 @@ namespace :openfoodnetwork do end end end + + def request_months + # Ask how many months back we want to search for + puts "This task will search order cycle edited within (n) months of today's date.\nPlease enter a value for (n), or hit ENTER to use the default of three (3) months." + input = check_default(STDIN.gets.chomp) + + while !is_integer?(input) + puts "'#{input}' is not an integer. Please enter an integer." + input = check_default(STDIN.gets.chomp) + end + + Integer(input) + end + + def check_default(input) + if input.blank? + puts "Using default value of three (3) months." + 3 + else + input + end + end + + def is_integer?(value) + return true if Integer(value) rescue false + end end end From 201bcb133af2278dc9e85117ba18cc661e8c0fb7 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 15 Apr 2015 16:15:21 +1000 Subject: [PATCH 359/384] show profiles checkbox on groups hubs page --- app/views/groups/show.html.haml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 625d4f0d6d..9bc40337a9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -79,6 +79,9 @@ = render partial: "shared/components/enterprise_search" -# TODO: find out why this is not working -#= render partial: "home/filters" + .small-12.medium-6.columns + %span   + = render partial: 'shared/components/show_profiles' .row{bindonce: true} .small-12.columns From 548e3576b9623c4ffba52186d963ec0c11a8578e Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 15 Apr 2015 17:13:10 +1000 Subject: [PATCH 360/384] textAngular editor for group description --- .../admin/enterprise_groups/enterprise_groups.js.coffee | 2 +- app/views/admin/enterprise_groups/_form_about.html.haml | 6 +++--- app/views/groups/show.html.haml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee b/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee index e8e462d998..0ff8e4f515 100644 --- a/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee +++ b/app/assets/javascripts/admin/enterprise_groups/enterprise_groups.js.coffee @@ -1 +1 @@ -angular.module("admin.enterprise_groups", ["admin.side_menu", "admin.users"]) +angular.module("admin.enterprise_groups", ["admin.side_menu", "admin.users", "textAngular"]) diff --git a/app/views/admin/enterprise_groups/_form_about.html.haml b/app/views/admin/enterprise_groups/_form_about.html.haml index 60d7276094..a29fde22cd 100644 --- a/app/views/admin/enterprise_groups/_form_about.html.haml +++ b/app/views/admin/enterprise_groups/_form_about.html.haml @@ -1,6 +1,6 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='About'" } } %legend About = f.field_container :long_description do - = f.label :long_description - %br/ - = f.text_area :long_description + %text-angular{'id' => 'enterprise_group_long_description', 'name' => 'enterprise_group[long_description]', 'class' => 'text-angular', + 'ta-toolbar' => "[['h1','h2','h3','h4','p'],['bold','italics','underline','clear'],['insertLink']]"} + != @enterprise_group[:long_description] diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 9bc40337a9..1bc965dfb3 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -40,7 +40,7 @@ active: "active(\'about\')", select: "select(\'about\')"} %h1 About Us - %p= @group.long_description + %p!= @group.long_description %tab{heading: 'Our producers', active: "active(\'producers\')", From 48bf87ec73179109c08767de5fe7f07de98eb1dc Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 1 Apr 2015 14:07:54 +1100 Subject: [PATCH 361/384] Perpetually updating copyright year --- app/views/shared/_footer.html.haml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/shared/_footer.html.haml b/app/views/shared/_footer.html.haml index 70eb14dd41..e06ae0f0aa 100644 --- a/app/views/shared/_footer.html.haml +++ b/app/views/shared/_footer.html.haml @@ -43,7 +43,9 @@ %h5.pad-top %a{title: 'Open Food Network', href:'http://www.openfoodnetwork.org', target: '_blank' } openfoodnetwork.org %br - © Copyright 2014 Open Food Foundation + © Copyright + = Date.today.year + Open Food Foundation %p %small %a{href:"https://creativecommons.org/licenses/by-sa/3.0/", target: "_blank" } Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) From 2e021941d1048401481f423fba03f126909f6047 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 11:14:56 +1000 Subject: [PATCH 362/384] Fix bug where new enterprises should be created as hubs, but aren't --- app/controllers/admin/enterprises_controller.rb | 2 +- spec/controllers/admin/enterprises_controller_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 408fa63e02..d4b3244525 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -150,7 +150,7 @@ module Admin def override_sells unless spree_current_user.admin? has_hub = spree_current_user.owned_enterprises.is_hub.any? - new_enterprise_is_producer = !!params[:enterprise][:is_primary_producer] + new_enterprise_is_producer = Enterprise.new(params[:enterprise]).is_primary_producer params[:enterprise][:sells] = (has_hub && !new_enterprise_is_producer) ? 'any' : 'none' end end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 4604520c00..3a40c8e501 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -20,7 +20,7 @@ module Admin describe "creating an enterprise" do let(:country) { Spree::Country.find_by_name 'Australia' } let(:state) { Spree::State.find_by_name 'Victoria' } - let(:enterprise_params) { {enterprise: {name: 'zzz', permalink: 'zzz', email: "bob@example.com", address_attributes: {address1: 'a', city: 'a', zipcode: 'a', country_id: country.id, state_id: state.id}}} } + let(:enterprise_params) { {enterprise: {name: 'zzz', permalink: 'zzz', is_primary_producer: '0', email: "bob@example.com", address_attributes: {address1: 'a', city: 'a', zipcode: 'a', country_id: country.id, state_id: state.id}}} } it "grants management permission if the current user is an enterprise user" do controller.stub spree_current_user: distributor_manager From e570352a1b60a1b6d7f1f547c9752187853b4fe7 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 11:36:48 +1000 Subject: [PATCH 363/384] Grant fewer permissions on enterprise creation --- app/models/enterprise.rb | 9 +++------ spec/models/enterprise_spec.rb | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 78a40b7c60..8ea6e6faa3 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -382,13 +382,12 @@ class Enterprise < ActiveRecord::Base enterprises = owner.owned_enterprises.where('enterprises.id != ?', self) # We grant permissions to all pre-existing hubs + hub_permissions = [:add_to_order_cycle] + hub_permissions << :create_variant_overrides if is_primary_producer enterprises.is_hub.each do |enterprise| EnterpriseRelationship.create!(parent: self, child: enterprise, - permissions_list: [:add_to_order_cycle, - :manage_products, - :edit_profile, - :create_variant_overrides]) + permissions_list: hub_permissions) end # All pre-existing producers grant permission to new hubs @@ -397,8 +396,6 @@ class Enterprise < ActiveRecord::Base EnterpriseRelationship.create!(parent: enterprise, child: self, permissions_list: [:add_to_order_cycle, - :manage_products, - :edit_profile, :create_variant_overrides]) end end diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 365bfd6ab0..1643a90126 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -606,11 +606,11 @@ describe Enterprise do hub2 end - it "creates links from the new producer to all hubs owned by the same user, granting all permissions" do + it "creates links from the new producer to all hubs owned by the same user, granting add_to_order_cycle and create_variant_overrides permissions" do producer1 - should_have_enterprise_relationship from: producer1, to: hub1, with: :all_permissions - should_have_enterprise_relationship from: producer1, to: hub2, with: :all_permissions + should_have_enterprise_relationship from: producer1, to: hub1, with: [:add_to_order_cycle, :create_variant_overrides] + should_have_enterprise_relationship from: producer1, to: hub2, with: [:add_to_order_cycle, :create_variant_overrides] end it "does not create any other links" do @@ -622,24 +622,24 @@ describe Enterprise do describe "when a new hub is created" do - it "it creates links to the hub, from all producers owned by the same user, granting all permissions" do + it "it creates links to the hub, from all producers owned by the same user, granting add_to_order_cycle and create_variant_overrides permissions" do producer1 producer2 hub1 - should_have_enterprise_relationship from: producer1, to: hub1, with: :all_permissions - should_have_enterprise_relationship from: producer2, to: hub1, with: :all_permissions + should_have_enterprise_relationship from: producer1, to: hub1, with: [:add_to_order_cycle, :create_variant_overrides] + should_have_enterprise_relationship from: producer2, to: hub1, with: [:add_to_order_cycle, :create_variant_overrides] end - it "creates links from the new hub to all hubs owned by the same user, granting all permissions" do + it "creates links from the new hub to all hubs owned by the same user, granting add_to_order_cycle permission" do hub1 hub2 hub3 - should_have_enterprise_relationship from: hub2, to: hub1, with: :all_permissions - should_have_enterprise_relationship from: hub3, to: hub1, with: :all_permissions - should_have_enterprise_relationship from: hub3, to: hub2, with: :all_permissions + should_have_enterprise_relationship from: hub2, to: hub1, with: [:add_to_order_cycle] + should_have_enterprise_relationship from: hub3, to: hub1, with: [:add_to_order_cycle] + should_have_enterprise_relationship from: hub3, to: hub2, with: [:add_to_order_cycle] end it "does not create any other links" do @@ -657,6 +657,8 @@ describe Enterprise do er.should_not be_nil if opts[:with] == :all_permissions er.permissions.map(&:name).sort.should == ['add_to_order_cycle', 'manage_products', 'edit_profile', 'create_variant_overrides'].sort + elsif opts.key? :with + er.permissions.map(&:name).sort.should == opts[:with].map(&:to_s).sort end end end From 34d1841d2289313c78b649c371ddeb916a4918c9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 12:13:43 +1000 Subject: [PATCH 364/384] Make Variant#full_name resilient to regex chars --- app/models/spree/variant_decorator.rb | 6 ++--- spec/models/spree/variant_spec.rb | 38 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 64fbe7110b..86a0b5fd69 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -76,9 +76,9 @@ Spree::Variant.class_eval do def full_name return unit_to_display if display_name.blank? - return display_name if display_name.scan(/#{unit_to_display}/i).any? - return unit_to_display if unit_to_display.scan(/#{display_name}/i).any? - display_name + " (" + unit_to_display + ")" + return display_name if display_name.downcase.include? unit_to_display.downcase + return unit_to_display if unit_to_display.downcase.include? display_name.downcase + "#{display_name} (#{unit_to_display})" end def name_to_display diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 85deecc6ea..5a82f04ba7 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -104,6 +104,44 @@ module Spree end end + describe "generating the full name" do + let(:v) { Variant.new } + + before do + v.stub(:display_name) { 'display_name' } + v.stub(:unit_to_display) { 'unit_to_display' } + end + + it "returns unit_to_display when display_name is blank" do + v.stub(:display_name) { '' } + v.full_name.should == 'unit_to_display' + end + + it "returns display_name when it contains unit_to_display" do + v.stub(:display_name) { 'DiSpLaY_name' } + v.stub(:unit_to_display) { 'name' } + v.full_name.should == 'DiSpLaY_name' + end + + it "returns unit_to_display when it contains display_name" do + v.stub(:display_name) { '_to_' } + v.stub(:unit_to_display) { 'unit_TO_display' } + v.full_name.should == 'unit_TO_display' + end + + it "returns a combination otherwise" do + v.stub(:display_name) { 'display_name' } + v.stub(:unit_to_display) { 'unit_to_display' } + v.full_name.should == 'display_name (unit_to_display)' + end + + it "is resilient to regex chars" do + v = Variant.new display_name: ")))" + v.stub(:unit_to_display) { ")))" } + v.full_name.should == ")))" + end + end + describe "calculating the price with enterprise fees" do it "returns the price plus the fees" do distributor = double(:distributor) From 7f6c02ca29aeb18ec4c186d9bc92305d0a4d22ef Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 14:24:25 +1000 Subject: [PATCH 365/384] Add User#customer_of to look up customers --- app/models/customer.rb | 3 +++ app/models/spree/user_decorator.rb | 7 +++++++ spec/factories.rb | 7 +++++++ spec/models/spree/user_spec.rb | 7 +++++++ 4 files changed, 24 insertions(+) diff --git a/app/models/customer.rb b/app/models/customer.rb index f09ffe2e7d..d3fa9e093f 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,7 +1,10 @@ class Customer < ActiveRecord::Base belongs_to :enterprise + belongs_to :user, :class_name => Spree.user_class validates :code, presence: true, uniqueness: {scope: :enterprise_id} validates :email, presence: true validates :enterprise_id, presence: true + + scope :of, ->(enterprise) { where(enterprise_id: enterprise) } end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 7f04293bfd..6bec218774 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -4,6 +4,7 @@ Spree.user_class.class_eval do has_many :owned_enterprises, class_name: 'Enterprise', foreign_key: :owner_id, inverse_of: :owner has_many :owned_groups, class_name: 'EnterpriseGroup', foreign_key: :owner_id, inverse_of: :owner has_one :cart + has_many :customers accepts_nested_attributes_for :enterprise_roles, :allow_destroy => true @@ -12,6 +13,7 @@ Spree.user_class.class_eval do validate :limit_owned_enterprises + def known_users if admin? Spree::User.scoped @@ -30,6 +32,10 @@ Spree.user_class.class_eval do end end + def customer_of(enterprise) + customers.of(enterprise).first + end + def send_signup_confirmation Spree::UserMailer.signup_confirmation(self).deliver end @@ -38,6 +44,7 @@ Spree.user_class.class_eval do owned_enterprises(:reload).size < enterprise_limit end + private def limit_owned_enterprises diff --git a/spec/factories.rb b/spec/factories.rb index 28ea617bd2..aa86af8563 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -203,6 +203,13 @@ FactoryGirl.define do create(:tax_rate, amount: proxy.tax_rate_amount, tax_category: product.tax_category, included_in_price: true, calculator: Spree::Calculator::DefaultTax.new, zone: proxy.zone) end end + + factory :customer, :class => Customer do + email { Faker::Internet.email } + enterprise + code 'abc123' + user + end end diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index 39692c2029..0a89e81b4c 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -36,7 +36,14 @@ describe Spree.user_class do expect(u1.owned_groups(:reload)).to match_array([g1, g2]) expect(u2.owned_groups(:reload)).to match_array([g3]) end + end + it "loads a user's customer representation at a particular enterprise" do + u = create(:user) + e = create(:enterprise) + c = create(:customer, user: u, enterprise: e) + + u.customer_of(e).should == c end end From 7f973604e52570aa93a0b608eb8106270a936ecd Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 14:29:02 +1000 Subject: [PATCH 366/384] Add columns to customer totals report: order cycle, payment method, customer code, tags, billing address --- .../admin/reports_controller_decorator.rb | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index b1f229038d..f148456813 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -525,18 +525,26 @@ Spree::Admin::ReportsController.class_eval do table_items = @line_items @include_blank = 'All' - header = ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant", "Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?", - "Shipping", "Delivery?", "Ship street", "Ship street 2", "Ship city", "Ship postcode", "Ship state", "Order notes", "SKU"] + header = ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant", + "Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?", + "Shipping", "Delivery?", + "Ship Street", "Ship Street 2", "Ship City", "Ship Postcode", "Ship State", + "Comments", "SKU", + "Order Cycle", "Payment Method", "Customer Code", "Tags", + "Billing Street 1", "Billing Street 2", "Billing City", "Billing Postcode", "Billing State" + ] rsa = proc { |line_items| line_items.first.order.shipping_method.andand.require_ship_address } - columns = [ proc { |line_items| line_items.first.order.distributor.name }, + columns = [ + proc { |line_items| line_items.first.order.distributor.name }, proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname }, proc { |line_items| line_items.first.order.email }, proc { |line_items| line_items.first.order.bill_address.phone }, proc { |line_items| line_items.first.variant.product.supplier.name }, proc { |line_items| line_items.first.variant.product.name }, proc { |line_items| line_items.first.variant.full_name }, + proc { |line_items| line_items.sum { |li| li.quantity } }, proc { |line_items| line_items.sum { |li| li.amount } }, proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } }, @@ -547,26 +555,40 @@ Spree::Admin::ReportsController.class_eval do proc { |line_items| line_items.first.order.shipping_method.andand.name }, proc { |line_items| rsa.call(line_items) ? 'Y' : 'N' }, + proc { |line_items| line_items.first.order.ship_address.andand.address1 if rsa.call(line_items) }, proc { |line_items| line_items.first.order.ship_address.andand.address2 if rsa.call(line_items) }, proc { |line_items| line_items.first.order.ship_address.andand.city if rsa.call(line_items) }, proc { |line_items| line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items) }, proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) }, - proc { |line_items| line_items.first.order.special_instructions } , - proc { |line_items| line_items.first.variant.product.sku } ] + proc { |line_items| "" }, + proc { |line_items| line_items.first.variant.product.sku }, + + proc { |line_items| line_items.first.order.order_cycle.andand.name }, + proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name }, + proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.code }, + proc { |line_items| "" }, + + proc { |line_items| line_items.first.order.bill_address.andand.address1 }, + proc { |line_items| line_items.first.order.bill_address.andand.address2 }, + proc { |line_items| line_items.first.order.bill_address.andand.city }, + proc { |line_items| line_items.first.order.bill_address.andand.zipcode }, + proc { |line_items| line_items.first.order.bill_address.andand.state } ] rules = [ { group_by: proc { |line_item| line_item.order.distributor }, sort_by: proc { |distributor| distributor.name } }, { group_by: proc { |line_item| line_item.order }, sort_by: proc { |order| order.bill_address.lastname + " " + order.bill_address.firstname }, - summary_columns: [ proc { |line_items| line_items.first.order.distributor.name }, + summary_columns: [ + proc { |line_items| line_items.first.order.distributor.name }, proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname }, proc { |line_items| "" }, proc { |line_items| "" }, proc { |line_items| "" }, proc { |line_items| "TOTAL" }, proc { |line_items| "" }, + proc { |line_items| "" }, proc { |line_items| line_items.sum { |li| li.amount } }, proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } }, @@ -577,14 +599,27 @@ Spree::Admin::ReportsController.class_eval do proc { |line_items| "" }, proc { |line_items| "" }, + proc { |line_items| "" }, proc { |line_items| "" }, proc { |line_items| "" }, proc { |line_items| "" }, proc { |line_items| "" }, + proc { |line_items| line_items.first.order.special_instructions } , proc { |line_items| "" }, - proc { |line_items| "" } ] }, + + proc { |line_items| line_items.first.order.order_cycle.andand.name }, + proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name }, + proc { |line_items| "" }, + proc { |line_items| "" }, + + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" } + ] }, { group_by: proc { |line_item| line_item.variant.product }, sort_by: proc { |product| product.name } }, From 768cfab59184c6fc6007aa79ea0eb9421e3b84b4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 15 Apr 2015 15:59:37 +1000 Subject: [PATCH 367/384] Change field text at checkout --- app/views/checkout/_shipping.html.haml | 2 +- spec/features/consumer/shopping/checkout_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/checkout/_shipping.html.haml b/app/views/checkout/_shipping.html.haml index a1c6e5ba9c..e769376107 100644 --- a/app/views/checkout/_shipping.html.haml +++ b/app/views/checkout/_shipping.html.haml @@ -46,7 +46,7 @@ .row .small-12.columns - = f.text_area :special_instructions, label: "Any notes or custom delivery instructions?", size: "60x4", "ng-model" => "order.special_instructions" + = f.text_area :special_instructions, label: "Any comments or special instructions?", size: "60x4", "ng-model" => "order.special_instructions" .row .small-12.columns.text-right diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index d46f0ae2f1..ac7fac09c5 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -112,7 +112,7 @@ feature "As a consumer I want to check out my cart", js: true do toggle_shipping within "#shipping" do choose sm2.name - fill_in 'Any notes or custom delivery instructions?', with: "SpEcIaL NoTeS" + fill_in 'Any comments or special instructions?', with: "SpEcIaL NoTeS" end toggle_payment within "#payment" do From 4672cc4863ef45b95cedaf120fdc66be346c24ff Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 15 Apr 2015 13:36:40 +1000 Subject: [PATCH 368/384] Add buildkite scripts --- script/push_to_production.sh | 10 ++++++++++ script/push_to_staging.sh | 10 ++++++++++ script/run_tests.sh | 12 ++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 script/push_to_production.sh create mode 100644 script/push_to_staging.sh create mode 100755 script/run_tests.sh diff --git a/script/push_to_production.sh b/script/push_to_production.sh new file mode 100644 index 0000000000..b4e374d744 --- /dev/null +++ b/script/push_to_production.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e + +PROD_TEST=`git remote | grep -s 'production' || true` +if [[ "$PROD_TEST" != *production* ]]; then + git remote add production ubuntu@ofn-prod:apps/openfoodweb/current +fi + +[[ $(git push production $BUILDKITE_COMMIT:master --force 2>&1) =~ "Done" ]] diff --git a/script/push_to_staging.sh b/script/push_to_staging.sh new file mode 100644 index 0000000000..e3a16ebb3b --- /dev/null +++ b/script/push_to_staging.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e + +ST2_TEST=`git remote | grep -s 'staging2' || true` +if [[ "$ST2_TEST" != *staging2* ]]; then + git remote add staging2 openfoodweb@ofn-staging2:apps/openfoodweb/current +fi + +[[ $(git push staging2 $BUILDKITE_COMMIT:master --force 2>&1) =~ "Done" ]] diff --git a/script/run_tests.sh b/script/run_tests.sh new file mode 100755 index 0000000000..1910c2ce1e --- /dev/null +++ b/script/run_tests.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +echo "--- Bundling" +bundle install + +echo "--- Preparing test database" +bundle exec rake db:test:prepare + +echo "--- Running tests" +bundle exec rake From 2b50f993de77e324bccf9d44b4c2e8d76e0c0e20 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 15 Apr 2015 13:54:12 +1000 Subject: [PATCH 369/384] Load environment before attempting build --- script/run_tests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/run_tests.sh b/script/run_tests.sh index 1910c2ce1e..17c0d37ec8 100755 --- a/script/run_tests.sh +++ b/script/run_tests.sh @@ -2,6 +2,9 @@ set -e +echo "--- Loading environment" +source /var/lib/jenkins/.rvm/environments/ruby-1.9.3-p392 + echo "--- Bundling" bundle install From 78bf87614a420ff2b87bf4327d90b4844f31100c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 15 Apr 2015 13:59:37 +1000 Subject: [PATCH 370/384] Buildkite: Symlink config/application.yml if not present --- script/run_tests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/run_tests.sh b/script/run_tests.sh index 17c0d37ec8..b1bc35b61a 100755 --- a/script/run_tests.sh +++ b/script/run_tests.sh @@ -4,6 +4,9 @@ set -e echo "--- Loading environment" source /var/lib/jenkins/.rvm/environments/ruby-1.9.3-p392 +if [ ! -f config/application.yml ]; then + ln -s config/application.yml.example config/application.yml +fi echo "--- Bundling" bundle install From ff07707af7df6f27865e738674db5c1e6bffaac7 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 15 Apr 2015 14:04:32 +1000 Subject: [PATCH 371/384] Symlink it right --- script/run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/run_tests.sh b/script/run_tests.sh index b1bc35b61a..883a12aec6 100755 --- a/script/run_tests.sh +++ b/script/run_tests.sh @@ -5,7 +5,7 @@ set -e echo "--- Loading environment" source /var/lib/jenkins/.rvm/environments/ruby-1.9.3-p392 if [ ! -f config/application.yml ]; then - ln -s config/application.yml.example config/application.yml + ln -s application.yml.example config/application.yml fi echo "--- Bundling" From 23888b7576d787f26b8b9b567cf854e88b12f24e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 15 Apr 2015 14:46:34 +1000 Subject: [PATCH 372/384] chmod +x --- script/push_to_production.sh | 0 script/push_to_staging.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 script/push_to_production.sh mode change 100644 => 100755 script/push_to_staging.sh diff --git a/script/push_to_production.sh b/script/push_to_production.sh old mode 100644 new mode 100755 diff --git a/script/push_to_staging.sh b/script/push_to_staging.sh old mode 100644 new mode 100755 From e017c30ab73e31839b9454fbb4d6627c40b118e8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 16 Apr 2015 10:58:36 +1000 Subject: [PATCH 373/384] Do not double-run db:test:prepare --- script/run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/run_tests.sh b/script/run_tests.sh index 883a12aec6..9082a79190 100755 --- a/script/run_tests.sh +++ b/script/run_tests.sh @@ -15,4 +15,4 @@ echo "--- Preparing test database" bundle exec rake db:test:prepare echo "--- Running tests" -bundle exec rake +bundle exec rspec spec From 318957551f4e09efa672c611c43268b5c233abde Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 16 Apr 2015 12:19:35 +1000 Subject: [PATCH 374/384] Freeze time to make spec repeatable --- .../admin/enterprises_controller_spec.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 4604520c00..c783f958c5 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -283,12 +283,14 @@ module Admin end it "is disallowed" do - spree_post :set_sells, { id: enterprise, sells: 'own' } - expect(response).to redirect_to spree.admin_path - trial_expiry = Date.today.strftime("%Y-%m-%d") - expect(flash[:error]).to eq "Sorry, but you've already had a trial. Expired on: #{trial_expiry}" - expect(enterprise.reload.sells).to eq 'own' - expect(enterprise.reload.shop_trial_start_date).to eq (Date.today - 30.days).to_time + Timecop.freeze(Time.zone.local(2015, 4, 16, 14, 0, 0)) do + spree_post :set_sells, { id: enterprise, sells: 'own' } + expect(response).to redirect_to spree.admin_path + trial_expiry = Date.today.strftime("%Y-%m-%d") + expect(flash[:error]).to eq "Sorry, but you've already had a trial. Expired on: #{trial_expiry}" + expect(enterprise.reload.sells).to eq 'own' + expect(enterprise.reload.shop_trial_start_date).to eq (Date.today - 30.days).to_time + end end end From 61cb2514cde8ffca17e4c287e16fbee872e504f5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 14:36:12 +1000 Subject: [PATCH 375/384] Add Delayed::Job --- Gemfile | 1 + Gemfile.lock | 6 +++++ .../20150410043302_create_delayed_jobs.rb | 22 +++++++++++++++++++ db/schema.rb | 19 +++++++++++++++- script/delayed_job | 5 +++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20150410043302_create_delayed_jobs.rb create mode 100755 script/delayed_job diff --git a/Gemfile b/Gemfile index 8998ac8882..4dcf92fc76 100644 --- a/Gemfile +++ b/Gemfile @@ -15,6 +15,7 @@ gem 'spree_auth_devise', :github => 'spree/spree_auth_devise', :branch => '1-3-s gem 'spree_paypal_express', :github => "openfoodfoundation/better_spree_paypal_express", :branch => "1-3-stable" #gem 'spree_paypal_express', :github => "spree-contrib/better_spree_paypal_express", :branch => "1-3-stable" +gem 'delayed_job_active_record' gem 'comfortable_mexican_sofa' # Fix bug in simple_form preventing collection_check_boxes usage within form_for block diff --git a/Gemfile.lock b/Gemfile.lock index 320e680184..f9522129c3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -219,6 +219,11 @@ GEM debugger-ruby_core_source (~> 1.2.3) debugger-linecache (1.2.0) debugger-ruby_core_source (1.2.3) + delayed_job (4.0.4) + activesupport (>= 3.0, < 4.2) + delayed_job_active_record (4.0.2) + activerecord (>= 3.0, < 4.2) + delayed_job (>= 3.0, < 4.1) devise (2.2.8) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) @@ -534,6 +539,7 @@ DEPENDENCIES db2fog debugger-linecache deface! + delayed_job_active_record factory_girl_rails figaro foreigner diff --git a/db/migrate/20150410043302_create_delayed_jobs.rb b/db/migrate/20150410043302_create_delayed_jobs.rb new file mode 100644 index 0000000000..f7de70bdc5 --- /dev/null +++ b/db/migrate/20150410043302_create_delayed_jobs.rb @@ -0,0 +1,22 @@ +class CreateDelayedJobs < ActiveRecord::Migration + def self.up + create_table :delayed_jobs, :force => true do |table| + table.integer :priority, :default => 0, :null => false # Allows some jobs to jump to the front of the queue + table.integer :attempts, :default => 0, :null => false # Provides for retries, but still fail eventually. + table.text :handler, :null => false # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.string :queue # The name of the queue this job is in + table.timestamps + end + + add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' + end + + def self.down + drop_table :delayed_jobs + end +end diff --git a/db/schema.rb b/db/schema.rb index 7435fa4e30..f30408c2ac 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150225232938) do +ActiveRecord::Schema.define(:version => 20150410043302) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -168,6 +168,22 @@ ActiveRecord::Schema.define(:version => 20150225232938) do add_index "customers", ["enterprise_id", "code"], :name => "index_customers_on_enterprise_id_and_code", :unique => true add_index "customers", ["user_id"], :name => "index_customers_on_user_id" + create_table "delayed_jobs", :force => true do |t| + t.integer "priority", :default => 0, :null => false + t.integer "attempts", :default => 0, :null => false + t.text "handler", :null => false + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" + create_table "distributors_payment_methods", :id => false, :force => true do |t| t.integer "distributor_id" t.integer "payment_method_id" @@ -298,6 +314,7 @@ ActiveRecord::Schema.define(:version => 20150225232938) do t.datetime "shop_trial_start_date" t.boolean "producer_profile_only", :default => false t.string "permalink", :null => false + t.boolean "charges_sales_tax", :default => false, :null => false end add_index "enterprises", ["address_id"], :name => "index_enterprises_on_address_id" diff --git a/script/delayed_job b/script/delayed_job new file mode 100755 index 0000000000..edf195985f --- /dev/null +++ b/script/delayed_job @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize From 1d5a4c93e465d23df4592f3c01bd60000346d5f9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 15:26:57 +1000 Subject: [PATCH 376/384] Add delayed job testing support, original credit Mat Holroyd and Rohan Mitchell, from CERES Fairfood project --- spec/support/delayed_job_support.rb | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 spec/support/delayed_job_support.rb diff --git a/spec/support/delayed_job_support.rb b/spec/support/delayed_job_support.rb new file mode 100644 index 0000000000..5a76803c8b --- /dev/null +++ b/spec/support/delayed_job_support.rb @@ -0,0 +1,58 @@ +module DelayedJobSupport + # Process all pending Delayed jobs, keeping in mind jobs could spawn new + # delayed job (so things might be added to the queue while processing) + def flush_jobs(options = {}) + options[:ignore_exceptions] ||= false + + Delayed::Worker.new.work_off(100) + + unless options[:ignore_exceptions] + Delayed::Job.all.each do |job| + if job.last_error.present? + throw "There was an error in a delayed job: #{job.last_error}" + end + end + end + end + + def clear_jobs + Delayed::Job.delete_all + end + + + # expect { foo }.to enqueue_job clazz: MyJob, field1: 'foo', field2: 'bar' + RSpec::Matchers.define :enqueue_job do |options = {}| + match do |event_proc| + last_job_id_before = Delayed::Job.last.id + + event_proc.call + + @jobs_created = Delayed::Job.where('id > ?', last_job_id_before) + + @jobs_created.any? do |job| + job = job.payload_object + + match = true + match &= (job.class == options[:clazz]) if options.key? :clazz + + options.each_pair do |k, v| + begin + match &= (job[k] == v) + rescue NameError + match = false unless k == :clazz + end + end + + match + end + end + + failure_message_for_should do |event_proc| + "expected job to be enqueued matching #{options.inspect} (#{@jobs_created.andand.count || '???'} others enqueued)" + end + + failure_message_for_should_not do |event_proc| + "expected job to not be enqueued matching #{options.inspect}" + end + end +end From 771b9e0df582a50d76447f6319c56ac6ed89bc81 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 15:28:46 +1000 Subject: [PATCH 377/384] Improve syntax: take job class as a separate argument --- spec/support/delayed_job_support.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/spec/support/delayed_job_support.rb b/spec/support/delayed_job_support.rb index 5a76803c8b..05c3417309 100644 --- a/spec/support/delayed_job_support.rb +++ b/spec/support/delayed_job_support.rb @@ -1,11 +1,11 @@ module DelayedJobSupport - # Process all pending Delayed jobs, keeping in mind jobs could spawn new + # Process all pending Delayed jobs, keeping in mind jobs could spawn new # delayed job (so things might be added to the queue while processing) def flush_jobs(options = {}) options[:ignore_exceptions] ||= false - + Delayed::Worker.new.work_off(100) - + unless options[:ignore_exceptions] Delayed::Job.all.each do |job| if job.last_error.present? @@ -15,13 +15,14 @@ module DelayedJobSupport end end + def clear_jobs Delayed::Job.delete_all end - # expect { foo }.to enqueue_job clazz: MyJob, field1: 'foo', field2: 'bar' - RSpec::Matchers.define :enqueue_job do |options = {}| + # expect { foo }.to enqueue_job MyJob, field1: 'foo', field2: 'bar' + RSpec::Matchers.define :enqueue_job do |klass, options = {}| match do |event_proc| last_job_id_before = Delayed::Job.last.id @@ -33,13 +34,13 @@ module DelayedJobSupport job = job.payload_object match = true - match &= (job.class == options[:clazz]) if options.key? :clazz + match &= (job.class == klass) options.each_pair do |k, v| begin match &= (job[k] == v) rescue NameError - match = false unless k == :clazz + match = false end end From 530740158ca7b6986c9801de895b34be7d2231ec Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 15:39:06 +1000 Subject: [PATCH 378/384] Tolerate no jobs previously in queue --- spec/support/delayed_job_support.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/delayed_job_support.rb b/spec/support/delayed_job_support.rb index 05c3417309..085478983e 100644 --- a/spec/support/delayed_job_support.rb +++ b/spec/support/delayed_job_support.rb @@ -24,7 +24,7 @@ module DelayedJobSupport # expect { foo }.to enqueue_job MyJob, field1: 'foo', field2: 'bar' RSpec::Matchers.define :enqueue_job do |klass, options = {}| match do |event_proc| - last_job_id_before = Delayed::Job.last.id + last_job_id_before = Delayed::Job.last.andand.id || 0 event_proc.call From 3bbf42c3e72f3b8f8372469754e44a6e0dab89bc Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 15:40:42 +1000 Subject: [PATCH 379/384] WIP: Rename delayed job support to delayed job helper --- spec/support/{delayed_job_support.rb => delayed_job_helper.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename spec/support/{delayed_job_support.rb => delayed_job_helper.rb} (100%) diff --git a/spec/support/delayed_job_support.rb b/spec/support/delayed_job_helper.rb similarity index 100% rename from spec/support/delayed_job_support.rb rename to spec/support/delayed_job_helper.rb From af4baabb50dc78f68479a00a2038bceb984cefcf Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 15:42:02 +1000 Subject: [PATCH 380/384] Include DelayedJobHelper for specs --- spec/spec_helper.rb | 1 + spec/support/delayed_job_helper.rb | 96 +++++++++++++++--------------- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3a7d2b3ca1..d5410a5026 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -93,6 +93,7 @@ RSpec.configure do |config| config.include OpenFoodNetwork::EnterpriseGroupsHelper config.include OpenFoodNetwork::DistributionHelper config.include ActionView::Helpers::DateHelper + config.include OpenFoodNetwork::DelayedJobHelper # FactoryGirl require 'factory_girl_rails' diff --git a/spec/support/delayed_job_helper.rb b/spec/support/delayed_job_helper.rb index 085478983e..1e94783586 100644 --- a/spec/support/delayed_job_helper.rb +++ b/spec/support/delayed_job_helper.rb @@ -1,59 +1,61 @@ -module DelayedJobSupport - # Process all pending Delayed jobs, keeping in mind jobs could spawn new - # delayed job (so things might be added to the queue while processing) - def flush_jobs(options = {}) - options[:ignore_exceptions] ||= false +module OpenFoodNetwork + module DelayedJobHelper + # Process all pending Delayed jobs, keeping in mind jobs could spawn new + # delayed job (so things might be added to the queue while processing) + def flush_jobs(options = {}) + options[:ignore_exceptions] ||= false - Delayed::Worker.new.work_off(100) + Delayed::Worker.new.work_off(100) - unless options[:ignore_exceptions] - Delayed::Job.all.each do |job| - if job.last_error.present? - throw "There was an error in a delayed job: #{job.last_error}" - end - end - end - end - - - def clear_jobs - Delayed::Job.delete_all - end - - - # expect { foo }.to enqueue_job MyJob, field1: 'foo', field2: 'bar' - RSpec::Matchers.define :enqueue_job do |klass, options = {}| - match do |event_proc| - last_job_id_before = Delayed::Job.last.andand.id || 0 - - event_proc.call - - @jobs_created = Delayed::Job.where('id > ?', last_job_id_before) - - @jobs_created.any? do |job| - job = job.payload_object - - match = true - match &= (job.class == klass) - - options.each_pair do |k, v| - begin - match &= (job[k] == v) - rescue NameError - match = false + unless options[:ignore_exceptions] + Delayed::Job.all.each do |job| + if job.last_error.present? + throw "There was an error in a delayed job: #{job.last_error}" end end - - match end end - failure_message_for_should do |event_proc| - "expected job to be enqueued matching #{options.inspect} (#{@jobs_created.andand.count || '???'} others enqueued)" + + def clear_jobs + Delayed::Job.delete_all end - failure_message_for_should_not do |event_proc| - "expected job to not be enqueued matching #{options.inspect}" + + # expect { foo }.to enqueue_job MyJob, field1: 'foo', field2: 'bar' + RSpec::Matchers.define :enqueue_job do |klass, options = {}| + match do |event_proc| + last_job_id_before = Delayed::Job.last.andand.id || 0 + + event_proc.call + + @jobs_created = Delayed::Job.where('id > ?', last_job_id_before) + + @jobs_created.any? do |job| + job = job.payload_object + + match = true + match &= (job.class == klass) + + options.each_pair do |k, v| + begin + match &= (job[k] == v) + rescue NameError + match = false + end + end + + match + end + end + + failure_message_for_should do |event_proc| + "expected job to be enqueued matching #{options.inspect} (#{@jobs_created.andand.count || '???'} others enqueued)" + end + + failure_message_for_should_not do |event_proc| + "expected job to not be enqueued matching #{options.inspect}" + end end end end From bb3bdf37cd446aa9c5f909b2c90a1e481d0cb417 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 15:51:42 +1000 Subject: [PATCH 381/384] Add jobs for user, order and enterprise emails --- app/jobs/confirm_order_job.rb | 6 ++++++ app/jobs/confirm_signup_job.rb | 6 ++++++ app/jobs/welcome_enterprise_job.rb | 6 ++++++ spec/jobs/confirm_order_job_spec.rb | 16 ++++++++++++++++ spec/jobs/confirm_signup_job_spec.rb | 13 +++++++++++++ spec/jobs/welcome_enterprise_job_spec.rb | 13 +++++++++++++ spec/support/delayed_job_helper.rb | 7 +++++++ 7 files changed, 67 insertions(+) create mode 100644 app/jobs/confirm_order_job.rb create mode 100644 app/jobs/confirm_signup_job.rb create mode 100644 app/jobs/welcome_enterprise_job.rb create mode 100644 spec/jobs/confirm_order_job_spec.rb create mode 100644 spec/jobs/confirm_signup_job_spec.rb create mode 100644 spec/jobs/welcome_enterprise_job_spec.rb diff --git a/app/jobs/confirm_order_job.rb b/app/jobs/confirm_order_job.rb new file mode 100644 index 0000000000..e16df2d99a --- /dev/null +++ b/app/jobs/confirm_order_job.rb @@ -0,0 +1,6 @@ +ConfirmOrderJob = Struct.new(:order_id) do + def perform + Spree::OrderMailer.confirm_email_for_customer(order_id).deliver + Spree::OrderMailer.confirm_email_for_shop(order_id).deliver + end +end diff --git a/app/jobs/confirm_signup_job.rb b/app/jobs/confirm_signup_job.rb new file mode 100644 index 0000000000..d16aee714c --- /dev/null +++ b/app/jobs/confirm_signup_job.rb @@ -0,0 +1,6 @@ +ConfirmSignupJob = Struct.new(:user_id) do + def perform + user = Spree::User.find user_id + Spree::UserMailer.signup_confirmation(user).deliver + end +end diff --git a/app/jobs/welcome_enterprise_job.rb b/app/jobs/welcome_enterprise_job.rb new file mode 100644 index 0000000000..6665af9d5a --- /dev/null +++ b/app/jobs/welcome_enterprise_job.rb @@ -0,0 +1,6 @@ +WelcomeEnterpriseJob = Struct.new(:enterprise_id) do + def perform + enterprise = Enterprise.find enterprise_id + EnterpriseMailer.welcome(enterprise).deliver + end +end diff --git a/spec/jobs/confirm_order_job_spec.rb b/spec/jobs/confirm_order_job_spec.rb new file mode 100644 index 0000000000..e3138620e3 --- /dev/null +++ b/spec/jobs/confirm_order_job_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe ConfirmOrderJob do + let(:order) { create(:order) } + + it "sends confirmation emails to both the user and the shop owner" do + customer_confirm_fake = double(:confirm_email_for_customer) + shop_confirm_fake = double(:confirm_email_for_shop) + expect(Spree::OrderMailer).to receive(:confirm_email_for_customer).and_return customer_confirm_fake + expect(Spree::OrderMailer).to receive(:confirm_email_for_shop).and_return shop_confirm_fake + expect(customer_confirm_fake).to receive :deliver + expect(shop_confirm_fake).to receive :deliver + + run_job ConfirmOrderJob.new order.id + end +end diff --git a/spec/jobs/confirm_signup_job_spec.rb b/spec/jobs/confirm_signup_job_spec.rb new file mode 100644 index 0000000000..4d7b97bcc0 --- /dev/null +++ b/spec/jobs/confirm_signup_job_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe ConfirmSignupJob do + let(:user) { create(:user) } + + it "sends a confirmation email to the user" do + mail = double(:mail) + Spree::UserMailer.should_receive(:signup_confirmation).with(user).and_return(mail) + mail.should_receive(:deliver) + + run_job ConfirmSignupJob.new user.id + end +end diff --git a/spec/jobs/welcome_enterprise_job_spec.rb b/spec/jobs/welcome_enterprise_job_spec.rb new file mode 100644 index 0000000000..051f4a31d1 --- /dev/null +++ b/spec/jobs/welcome_enterprise_job_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe WelcomeEnterpriseJob do + let(:enterprise) { create(:enterprise) } + + it "sends a welcome email to the enterprise" do + mail = double(:mail) + EnterpriseMailer.should_receive(:welcome).with(enterprise).and_return(mail) + mail.should_receive(:deliver) + + run_job WelcomeEnterpriseJob.new(enterprise.id) + end +end diff --git a/spec/support/delayed_job_helper.rb b/spec/support/delayed_job_helper.rb index 1e94783586..5f9cfb6140 100644 --- a/spec/support/delayed_job_helper.rb +++ b/spec/support/delayed_job_helper.rb @@ -1,5 +1,12 @@ module OpenFoodNetwork module DelayedJobHelper + def run_job(job) + clear_jobs + Delayed::Job.enqueue job + flush_jobs + end + + # Process all pending Delayed jobs, keeping in mind jobs could spawn new # delayed job (so things might be added to the queue while processing) def flush_jobs(options = {}) From e9fbd74e888ab5e07932db95a13b45d77b7f3322 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 15:54:03 +1000 Subject: [PATCH 382/384] Send emails via jobs instead of synchronously --- app/models/enterprise.rb | 2 +- app/models/spree/order_decorator.rb | 9 +-------- app/models/spree/user_decorator.rb | 2 +- spec/models/enterprise_spec.rb | 14 ++++++-------- spec/models/spree/order_spec.rb | 12 ++++-------- spec/models/spree/user_spec.rb | 5 +++-- 6 files changed, 16 insertions(+), 28 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 8ea6e6faa3..97766078b1 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -348,7 +348,7 @@ class Enterprise < ActiveRecord::Base end def send_welcome_email - EnterpriseMailer.welcome(self).deliver + Delayed::Job.enqueue WelcomeEnterpriseJob.new(self.id) end def strip_url(url) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 9d213bd633..d5e18c3d9b 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -216,14 +216,7 @@ Spree::Order.class_eval do # Overrride of Spree method, that allows us to send separate confirmation emails to user and shop owners def deliver_order_confirmation_email - begin - Spree::OrderMailer.confirm_email_for_customer(self.id).deliver - Spree::OrderMailer.confirm_email_for_shop(self.id).deliver - rescue Exception => e - Bugsnag.notify(e) - logger.error("#{e.class.name}: #{e.message}") - logger.error(e.backtrace * "\n") - end + Delayed::Job.enqueue ConfirmOrderJob.new(id) end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 6bec218774..b700fae3f0 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -37,7 +37,7 @@ Spree.user_class.class_eval do end def send_signup_confirmation - Spree::UserMailer.signup_confirmation(self).deliver + Delayed::Job.enqueue ConfirmSignupJob.new(id) end def can_own_more_enterprises? diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 1643a90126..97a4b988d8 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -29,10 +29,9 @@ describe Enterprise do end it "sends a welcome email" do - mail_message = double "Mail::Message" - expect(EnterpriseMailer).to receive(:welcome).and_return mail_message - mail_message.should_receive :deliver - create(:enterprise, owner: user, email: enterprise.email, confirmed_at: nil) + expect do + create(:enterprise, owner: user, email: enterprise.email, confirmed_at: nil) + end.to enqueue_job WelcomeEnterpriseJob end end end @@ -65,10 +64,9 @@ describe Enterprise do unconfirmed_enterprise.unconfirmed_email = nil unconfirmed_enterprise.save! - mail_message = double "Mail::Message" - expect(EnterpriseMailer).to receive(:welcome).and_return mail_message - mail_message.should_receive :deliver - unconfirmed_enterprise.confirm! + expect do + unconfirmed_enterprise.confirm! + end.to enqueue_job WelcomeEnterpriseJob, enterprise_id: unconfirmed_enterprise.id end end diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index df5850f823..3c70c2243c 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -452,14 +452,10 @@ describe Spree::Order do end describe "sending confirmation emails" do - it "sends confirmation emails to both the user and the shop owner" do - customer_confirm_fake = double(:confirm_email_for_customer) - shop_confirm_fake = double(:confirm_email_for_shop) - expect(Spree::OrderMailer).to receive(:confirm_email_for_customer).and_return customer_confirm_fake - expect(Spree::OrderMailer).to receive(:confirm_email_for_shop).and_return shop_confirm_fake - expect(customer_confirm_fake).to receive :deliver - expect(shop_confirm_fake).to receive :deliver - create(:order).deliver_order_confirmation_email + it "sends confirmation emails" do + expect do + create(:order).deliver_order_confirmation_email + end.to enqueue_job ConfirmOrderJob end end end diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index 0a89e81b4c..848febb9c5 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -49,8 +49,9 @@ describe Spree.user_class do context "#create" do it "should send a signup email" do - Spree::UserMailer.should_receive(:signup_confirmation).and_return(double(:deliver => true)) - create(:user) + expect do + create(:user) + end.to enqueue_job ConfirmSignupJob end end From ccc1f3df40f46eb60ec1635cec77fb5f3b11d0b9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Apr 2015 17:06:18 +1000 Subject: [PATCH 383/384] Feature specs test email sending through delayed jobs --- spec/features/consumer/authentication_spec.rb | 10 ++++---- .../consumer/shopping/checkout_spec.rb | 23 ++++++++----------- .../shopping/variant_overrides_spec.rb | 8 +------ 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb index 2aefb42db5..e3fa340909 100644 --- a/spec/features/consumer/authentication_spec.rb +++ b/spec/features/consumer/authentication_spec.rb @@ -45,9 +45,9 @@ feature "Authentication", js: true do describe "signing up" do before do - ActionMailer::Base.deliveries.clear select_login_tab "Sign up" end + scenario "Failing to sign up because password is too short" do fill_in "Email", with: "test@foo.com" fill_in "Choose a password", with: "short" @@ -59,10 +59,11 @@ feature "Authentication", js: true do fill_in "Email", with: "test@foo.com" fill_in "Choose a password", with: "test12345" fill_in "Confirm password", with: "test12345" - click_signup_button - page.should have_content "Welcome! You have signed up successfully" + expect do + click_signup_button + page.should have_content "Welcome! You have signed up successfully" + end.to enqueue_job ConfirmSignupJob page.should be_logged_in_as "test@foo.com" - ActionMailer::Base.deliveries.last.subject.should =~ /Welcome to/ end end @@ -105,4 +106,3 @@ feature "Authentication", js: true do end end end - diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index ac7fac09c5..12eca09339 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -16,7 +16,6 @@ feature "As a consumer I want to check out my cart", js: true do let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } before do - ActionMailer::Base.deliveries.clear add_enterprise_fee enterprise_fee set_order order add_product_to_cart @@ -80,7 +79,6 @@ feature "As a consumer I want to check out my cart", js: true do context "on the checkout page with payments open" do before do - ActionMailer::Base.deliveries.clear visit checkout_path checkout_as_guest toggle_payment @@ -120,13 +118,11 @@ feature "As a consumer I want to check out my cart", js: true do end - ActionMailer::Base.deliveries.length.should == 0 - place_order - page.should have_content "Your order has been processed successfully" - ActionMailer::Base.deliveries.length.should == 2 - email = ActionMailer::Base.deliveries.last - site_name = Spree::Config[:site_name] - email.subject.should include "#{site_name} Order Confirmation" + expect do + place_order + page.should have_content "Your order has been processed successfully" + end.to enqueue_job ConfirmOrderJob + o = Spree::Order.complete.first expect(o.special_instructions).to eq "SpEcIaL NoTeS" end @@ -214,7 +210,6 @@ feature "As a consumer I want to check out my cart", js: true do context "when the customer has a pre-set shipping and billing address" do before do - ActionMailer::Base.deliveries.clear # Load up the customer's order and give them a shipping and billing address # This is equivalent to when the customer has ordered before and their addresses # are pre-populated. @@ -231,10 +226,10 @@ feature "As a consumer I want to check out my cart", js: true do toggle_payment choose pm1.name - expect(ActionMailer::Base.deliveries.length).to be 0 - place_order - page.should have_content "Your order has been processed successfully" - expect(ActionMailer::Base.deliveries.length).to be 2 + expect do + place_order + page.should have_content "Your order has been processed successfully" + end.to enqueue_job ConfirmOrderJob end end end diff --git a/spec/features/consumer/shopping/variant_overrides_spec.rb b/spec/features/consumer/shopping/variant_overrides_spec.rb index 4178aeb2cc..98d58e174f 100644 --- a/spec/features/consumer/shopping/variant_overrides_spec.rb +++ b/spec/features/consumer/shopping/variant_overrides_spec.rb @@ -26,9 +26,9 @@ feature "shopping with variant overrides defined", js: true do let(:ef) { create(:enterprise_fee, enterprise: hub, fee_type: 'packing', calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)) } before do - ActionMailer::Base.deliveries.clear outgoing_exchange.variants = [v1, v2, v3, v4] outgoing_exchange.enterprise_fees << ef + sm.calculator.preferred_amount = 0 visit shop_path click_link hub.name end @@ -109,15 +109,9 @@ feature "shopping with variant overrides defined", js: true do complete_checkout - ActionMailer::Base.deliveries.length.should == 2 - email = ActionMailer::Base.deliveries.last - o = Spree::Order.complete.last - o.line_items.first.price.should == 55.55 o.total.should == 122.21 - - email.body.should include "$122.21" end it "subtracts stock from the override" do From d1ab2d9dac1dba796794838d1eaaa0e96bbef1a7 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 15 Apr 2015 09:25:00 +1000 Subject: [PATCH 384/384] Add daemons gem for Delayed::Job --- Gemfile | 1 + Gemfile.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index 4dcf92fc76..6d3c7262c8 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,7 @@ gem 'spree_paypal_express', :github => "openfoodfoundation/better_spree_paypal_e #gem 'spree_paypal_express', :github => "spree-contrib/better_spree_paypal_express", :branch => "1-3-stable" gem 'delayed_job_active_record' +gem 'daemons' gem 'comfortable_mexican_sofa' # Fix bug in simple_form preventing collection_check_boxes usage within form_for block diff --git a/Gemfile.lock b/Gemfile.lock index f9522129c3..d2a20aed31 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -207,6 +207,7 @@ GEM safe_yaml (~> 0.9.0) css_parser (1.3.5) addressable + daemons (1.2.2) dalli (2.7.2) database_cleaner (0.7.1) db2fog (0.8.0) @@ -534,6 +535,7 @@ DEPENDENCIES comfortable_mexican_sofa compass-rails custom_error_message! + daemons dalli database_cleaner (= 0.7.1) db2fog