diff --git a/app/assets/javascripts/admin/index_utils/directives/obj_for_update.js.coffee b/app/assets/javascripts/admin/index_utils/directives/obj_for_update.js.coffee index 81cf58fd1e..18c800ce7f 100644 --- a/app/assets/javascripts/admin/index_utils/directives/obj_for_update.js.coffee +++ b/app/assets/javascripts/admin/index_utils/directives/obj_for_update.js.coffee @@ -24,7 +24,7 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi scope.savedValue = value scope.success = -> - switchClass( element, "update-success", ["update-pending", "update-error"], 3000 ) + switchClass( element, "update-success", ["update-pending", "update-error"], 5000 ) scope.pending = -> switchClass( element, "update-pending", ["update-error", "update-success"], false ) diff --git a/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee index 8fd47c49f8..91cbe0bf54 100644 --- a/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee @@ -1,3 +1,7 @@ -Darkswarm.controller "GroupsCtrl", ($scope, Groups) -> +Darkswarm.controller "GroupsCtrl", ($scope, Groups, Search) -> $scope.Groups = Groups $scope.order = 'position' + $scope.query = Search.search() + + $scope.$watch "query", (query)-> + Search.search query diff --git a/app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee index fa6378afe2..099f2a39e4 100644 --- a/app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee @@ -1,9 +1,15 @@ -Darkswarm.directive "enterpriseModal", ($modal)-> +Darkswarm.directive "enterpriseModal", ($modal, Enterprises, EnterpriseResource) -> restrict: 'E' replace: true template: "" transclude: true - link: (scope, elem, attrs, ctrl)-> - elem.on "click", (ev)=> + link: (scope, elem, attrs, ctrl) -> + elem.on "click", (ev) => ev.stopPropagation() + params = + id: scope.enterprise.id + EnterpriseResource.relatives params, (data) => + Enterprises.addEnterprises data + scope.enterprise = Enterprises.enterprises_by_id[scope.enterprise.id] + Enterprises.dereferenceEnterprise scope.enterprise scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'enterprise_modal.html', scope: scope) diff --git a/app/assets/javascripts/darkswarm/directives/map_osm_tiles.js.coffee b/app/assets/javascripts/darkswarm/directives/map_osm_tiles.js.coffee new file mode 100644 index 0000000000..0dcda8f59a --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/map_osm_tiles.js.coffee @@ -0,0 +1,21 @@ +Darkswarm.directive 'mapOsmTiles', ($timeout) -> + restrict: 'E' + require: '^googleMap' + scope: {} + link: (scope, elem, attrs, ctrl) -> + $timeout => + map = ctrl.getMap() + + map.mapTypes.set 'OSM', new google.maps.ImageMapType + getTileUrl: (coord, zoom) -> + # "Wrap" x (logitude) at 180th meridian properly + # NB: Don't touch coord.x because coord param is by reference, and changing its x property breaks something in Google's lib + tilesPerGlobe = 1 << zoom + x = coord.x % tilesPerGlobe + if x < 0 + x = tilesPerGlobe + x + # Wrap y (latitude) in a like manner if you want to enable vertical infinite scroll + 'http://tile.openstreetmap.org/' + zoom + '/' + x + '/' + coord.y + '.png' + tileSize: new google.maps.Size(256, 256) + name: 'OpenStreetMap' + maxZoom: 18 diff --git a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee index e67ce529a5..af82766d62 100644 --- a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee @@ -1,46 +1,54 @@ -Darkswarm.directive 'mapSearch', ($timeout)-> +Darkswarm.directive 'mapSearch', ($timeout, Search) -> # Install a basic search field in a map restrict: 'E' - require: '^googleMap' + require: ['^googleMap', 'ngModel'] replace: true - template: '' - link: (scope, elem, attrs, ctrl)-> + template: '' + scope: {} + + controller: ($scope) -> + $scope.query = Search.search() + + $scope.$watch 'query', (query) -> + Search.search query + + + link: (scope, elem, attrs, ctrls) -> + [ctrl, model] = ctrls + scope.input = document.getElementById("pac-input") + $timeout => map = ctrl.getMap() - # Use OSM tiles server - map.mapTypes.set 'OSM', new (google.maps.ImageMapType)( - getTileUrl: (coord, zoom) -> - # "Wrap" x (logitude) at 180th meridian properly - # NB: Don't touch coord.x because coord param is by reference, and changing its x property breakes something in Google's lib - tilesPerGlobe = 1 << zoom - x = coord.x % tilesPerGlobe - if x < 0 - x = tilesPerGlobe + x - # Wrap y (latitude) in a like manner if you want to enable vertical infinite scroll - 'http://tile.openstreetmap.org/' + zoom + '/' + x + '/' + coord.y + '.png' - tileSize: new (google.maps.Size)(256, 256) - name: 'OpenStreetMap' - maxZoom: 18) + searchBox = scope.createSearchBox map + scope.bindSearchResponse map, searchBox + scope.biasResults map, searchBox + scope.performUrlSearch map - input = (document.getElementById("pac-input")) - map.controls[google.maps.ControlPosition.TOP_LEFT].push input - searchBox = new google.maps.places.SearchBox((input)) + scope.createSearchBox = (map) -> + map.controls[google.maps.ControlPosition.TOP_LEFT].push scope.input + return new google.maps.places.SearchBox(scope.input) - google.maps.event.addListener searchBox, "places_changed", -> - places = searchBox.getPlaces() - return if places.length is 0 - # For each place, get the icon, place name, and location. - markers = [] - bounds = new google.maps.LatLngBounds() - for place in places - #map.setCenter place.geometry.location - map.fitBounds place.geometry.viewport - #map.fitBounds bounds + scope.bindSearchResponse = (map, searchBox) -> + google.maps.event.addListener searchBox, "places_changed", => + scope.showSearchResult map, searchBox - # Bias the SearchBox results towards places that are within the bounds of the - # current map's viewport. + scope.showSearchResult = (map, searchBox) -> + places = searchBox.getPlaces() + for place in places when place.geometry.viewport? + map.fitBounds place.geometry.viewport + scope.$apply -> + model.$setViewValue elem.val() + + # When the map loads, and we have a search from ?query, perform that search + scope.performUrlSearch = (map) -> + google.maps.event.addListenerOnce map, "idle", => + google.maps.event.trigger(scope.input, 'focus'); + google.maps.event.trigger(scope.input, 'keydown', {keyCode: 13}); + + # Bias the SearchBox results towards places that are within the bounds of the + # current map's viewport. + scope.biasResults = (map, searchBox) -> google.maps.event.addListener map, "bounds_changed", -> bounds = map.getBounds() searchBox.setBounds bounds - diff --git a/app/assets/javascripts/darkswarm/filters/filter_products.js.coffee b/app/assets/javascripts/darkswarm/filters/filter_products.js.coffee index c01a941659..f65c8854dc 100644 --- a/app/assets/javascripts/darkswarm/filters/filter_products.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/filter_products.js.coffee @@ -1,7 +1,7 @@ -Darkswarm.filter 'products', (Matcher)-> - (products, text)-> +Darkswarm.filter 'products', (Matcher) -> + (products, text) -> products ||= [] text ?= "" - products.filter (product)=> + products.filter (product) => propertiesToMatch = [product.name, product.supplier.name, product.primary_taxon.name] Matcher.match propertiesToMatch, text diff --git a/app/assets/javascripts/darkswarm/services/dereferencer.js.coffee b/app/assets/javascripts/darkswarm/services/dereferencer.js.coffee index 2061f95ea1..b68a716a4d 100644 --- a/app/assets/javascripts/darkswarm/services/dereferencer.js.coffee +++ b/app/assets/javascripts/darkswarm/services/dereferencer.js.coffee @@ -1,6 +1,17 @@ Darkswarm.factory 'Dereferencer', -> new class Dereferencer - dereference: (array, data)-> - if array - for object, i in array - array[i] = data[object.id] + dereference: (array, data) -> + @dereference_from(array, array, data) + + dereference_from: (source, target, data) -> + unreferenced = [] + if source && target + for object, i in source + # skip empty entries in sparse array + continue unless source.hasOwnProperty(i) + key = object?.id + if data.hasOwnProperty(key) + target[i] = data[key] + else + unreferenced[i] = object + unreferenced diff --git a/app/assets/javascripts/darkswarm/services/enterprise_resource.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_resource.js.coffee new file mode 100644 index 0000000000..e66ec7c176 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/enterprise_resource.js.coffee @@ -0,0 +1,8 @@ +Darkswarm.factory 'EnterpriseResource', ($resource) -> + $resource('/enterprise/:id.json', {}, { + 'relatives': + method: 'GET' + url: '/enterprises/:id/relatives.json' + isArray: true + cache: true + }) diff --git a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee index 2dddfbc7ef..73dbd1627b 100644 --- a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee +++ b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope)-> +Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope) -> new class Enterprises enterprises_by_id: {} constructor: -> @@ -9,7 +9,6 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, @enterprises_by_id[enterprise.id] = enterprise # Replace enterprise and taxons ids with actual objects. @dereferenceEnterprises() - @dereferenceTaxons() @visible_enterprises = visibleFilter @enterprises @producers = @visible_enterprises.filter (enterprise)-> enterprise.category in ["producer_hub", "producer_shop", "producer"] @@ -20,13 +19,27 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, if CurrentHub.hub?.id CurrentHub.hub = @enterprises_by_id[CurrentHub.hub.id] for enterprise in @enterprises - Dereferencer.dereference enterprise.hubs, @enterprises_by_id - Dereferencer.dereference enterprise.producers, @enterprises_by_id + @dereferenceEnterprise enterprise - dereferenceTaxons: -> - for enterprise in @enterprises - Dereferencer.dereference enterprise.taxons, Taxons.taxons_by_id - Dereferencer.dereference enterprise.supplied_taxons, Taxons.taxons_by_id + dereferenceEnterprise: (enterprise) -> + @dereferenceProperty(enterprise, 'hubs', @enterprises_by_id) + @dereferenceProperty(enterprise, 'producers', @enterprises_by_id) + @dereferenceProperty(enterprise, 'taxons', Taxons.taxons_by_id) + @dereferenceProperty(enterprise, 'supplied_taxons', Taxons.taxons_by_id) + + dereferenceProperty: (enterprise, property, data) -> + # keep unreferenced enterprise ids + # in case we dereference again after adding more enterprises + enterprise.unreferenced |= {} + collection = enterprise[property] + unreferenced = enterprise.unreferenced[property] || collection + enterprise.unreferenced[property] = + Dereferencer.dereference_from unreferenced, collection, data + + addEnterprises: (new_enterprises) -> + return unless new_enterprises && new_enterprises.length + for enterprise in new_enterprises + @enterprises_by_id[enterprise.id] = enterprise flagMatching: (query) -> for enterprise in @enterprises diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index 7f627ec73a..70d240abd1 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -1,3 +1,5 @@ +require 'open_food_network/enterprise_injection_data' + class EnterprisesController < BaseController layout "darkswarm" helper Spree::ProductsHelper @@ -11,11 +13,22 @@ class EnterprisesController < BaseController respond_to :js, only: :permalink_checker + def relatives + respond_to do |format| + format.json do + enterprise = Enterprise.find(params[:id]) + enterprises = enterprise.andand.relatives.andand.activated + render(json: enterprises, + each_serializer: Api::EnterpriseSerializer, + data: OpenFoodNetwork::EnterpriseInjectionData.new) + end + end + end def check_permalink return render text: params[:permalink], status: 409 if Enterprise.find_by_permalink params[:permalink] - path = Rails.application.routes.recognize_path( "/#{ params[:permalink].to_s }" ) + path = Rails.application.routes.recognize_path("/#{params[:permalink].to_s}") if path && path[:controller] == "cms_content" render text: params[:permalink], status: 200 else @@ -23,7 +36,6 @@ class EnterprisesController < BaseController end end - private def clean_permalink diff --git a/app/helpers/checkout_helper.rb b/app/helpers/checkout_helper.rb index 66d98d7862..6359cb01bd 100644 --- a/app/helpers/checkout_helper.rb +++ b/app/helpers/checkout_helper.rb @@ -6,6 +6,7 @@ module CheckoutHelper # Remove empty tax adjustments and (optionally) shipping fees adjustments.reject! { |a| a.originator_type == 'Spree::TaxRate' && a.amount == 0 } adjustments.reject! { |a| a.originator_type == 'Spree::ShippingMethod' } if exclude.include? :shipping + adjustments.reject! { |a| a.originator_type == 'Spree::PaymentMethod' } if exclude.include? :payment adjustments.reject! { |a| a.source_type == 'Spree::LineItem' } if exclude.include? :line_item enterprise_fee_adjustments = adjustments.select { |a| a.originator_type == 'EnterpriseFee' && a.source_type != 'Spree::LineItem' } diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 50b9c93fca..6db2e583bc 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -5,6 +5,19 @@ module InjectionHelper inject_json_ams "enterprises", Enterprise.activated.includes(address: :state).all, Api::EnterpriseSerializer, enterprise_injection_data end + def inject_enterprise_and_relatives + inject_json_ams "enterprises", current_distributor.relatives_including_self.activated.includes(address: :state).all, Api::EnterpriseSerializer, enterprise_injection_data + end + + def inject_shop_enterprises + ocs = if current_order_cycle + [current_order_cycle] + else + OrderCycle.not_closed.with_distributor(current_distributor) + end + inject_json_ams "enterprises", current_distributor.plus_relatives_and_oc_producers(ocs).activated.includes(address: :state).all, Api::EnterpriseSerializer, enterprise_injection_data + end + def inject_group_enterprises inject_json_ams "group_enterprises", @group.enterprises.activated.all, Api::EnterpriseSerializer, enterprise_injection_data end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 554529d7f2..80414d5116 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -176,6 +176,16 @@ class Enterprise < ActiveRecord::Base joins(:enterprise_roles).where('enterprise_roles.user_id = ?', user.id) end } + scope :relatives_of_one_union_others, lambda { |one, others| + where(" + enterprises.id IN + (SELECT child_id FROM enterprise_relationships WHERE enterprise_relationships.parent_id=?) + OR enterprises.id IN + (SELECT parent_id FROM enterprise_relationships WHERE enterprise_relationships.child_id=?) + OR enterprises.id IN + (?) + ", one, one, others) + } # Force a distinct count to work around relation count issue https://github.com/rails/rails/issues/5554 def self.distinct_count @@ -220,6 +230,11 @@ class Enterprise < ActiveRecord::Base ", self.id, self.id) end + def plus_relatives_and_oc_producers(order_cycles) + oc_producer_ids = Exchange.in_order_cycle(order_cycles).incoming.pluck :sender_id + Enterprise.relatives_of_one_union_others(id, oc_producer_ids | [id]) + end + def relatives_including_self Enterprise.where(id: relatives.pluck(:id) | [id]) end diff --git a/app/models/spree/payment_decorator.rb b/app/models/spree/payment_decorator.rb index d178e1de54..dbee3cfd0f 100644 --- a/app/models/spree/payment_decorator.rb +++ b/app/models/spree/payment_decorator.rb @@ -5,7 +5,12 @@ module Spree after_save :ensure_correct_adjustment, :update_order def ensure_correct_adjustment - if adjustment + # Don't charge for invalid payments. + # PayPalExpress always creates a payment that is invalidated later. + # Unknown: What about failed payments? + if state == "invalid" + adjustment.andand.destroy + elsif adjustment adjustment.originator = payment_method adjustment.label = adjustment_label adjustment.save diff --git a/app/views/checkout/_summary.html.haml b/app/views/checkout/_summary.html.haml index a3b61c5ded..0fec419556 100644 --- a/app/views/checkout/_summary.html.haml +++ b/app/views/checkout/_summary.html.haml @@ -9,7 +9,7 @@ = t :checkout_cart_total %td.cart-total.text-right= display_checkout_subtotal(@order) - - checkout_adjustments_for(current_order, exclude: [:shipping, :line_item]).reject{ |a| a.amount == 0 }.each do |adjustment| + - checkout_adjustments_for(current_order, exclude: [:shipping, :payment, :line_item]).reject{ |a| a.amount == 0 }.each do |adjustment| %tr %th= adjustment.label %td.text-right= adjustment.display_amount.to_html diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index f0bef536db..b3d30ed442 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -1,7 +1,7 @@ - content_for(:title) do = t :checkout_title -= inject_enterprises += inject_enterprise_and_relatives .darkswarm.footer-pad - content_for :order_cycle_form do diff --git a/app/views/enterprises/shop.html.haml b/app/views/enterprises/shop.html.haml index d635f68164..569009db09 100644 --- a/app/views/enterprises/shop.html.haml +++ b/app/views/enterprises/shop.html.haml @@ -5,7 +5,7 @@ - content_for(:image) do = current_distributor.logo.url -= inject_enterprises += inject_shop_enterprises %shop.darkswarm - content_for :order_cycle_form do diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index af497a4195..a21bdb7460 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -40,6 +40,7 @@ .map-container %map{"ng-if" => "(isActive(\'/map\') && (mapShowed = true)) || mapShowed"} %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} + %map-osm-tiles %map-search %markers{models: "mapMarkers", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} diff --git a/app/views/map/index.html.haml b/app/views/map/index.html.haml index e4a22e540c..75bb99f9d7 100644 --- a/app/views/map/index.html.haml +++ b/app/views/map/index.html.haml @@ -6,6 +6,7 @@ .map-container{"fill-vertical" => true} %map{"ng-controller" => "MapCtrl"} %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} + %map-osm-tiles %map-search %markers{models: "OfnMap.enterprises", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} diff --git a/app/views/spree/orders/edit.html.haml b/app/views/spree/orders/edit.html.haml index 2fa5c043db..3417eb5e6a 100644 --- a/app/views/spree/orders/edit.html.haml +++ b/app/views/spree/orders/edit.html.haml @@ -1,7 +1,7 @@ - content_for(:title) do = t :orders_edit_title -= inject_enterprises += inject_enterprise_and_relatives .darkswarm - content_for :order_cycle_form do diff --git a/app/views/spree/orders/show.html.haml b/app/views/spree/orders/show.html.haml index 438a611a68..e4cb3b1980 100644 --- a/app/views/spree/orders/show.html.haml +++ b/app/views/spree/orders/show.html.haml @@ -1,17 +1,9 @@ - content_for(:title) do = t :orders_show_title -= inject_enterprises += inject_enterprise_and_relatives if current_distributor.present? .darkswarm - - content_for :order_cycle_form do - %strong.avenir - = t :orders_show_time - - if @order.order_cycle - = @order.order_cycle.pickup_time_for(@order.distributor) - - else - = @order.distributor.next_collection_at - = render "shopping_shared/details" if current_distributor.present? %fieldset#order_summary.footer-pad{"data-hook" => ""} diff --git a/config/locales/en.yml b/config/locales/en.yml index a752944e5c..9f1b6cfabc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -66,6 +66,7 @@ en: plus_tax: "plus GST" total_monthly_bill_incl_tax: "Total Monthly Bill (Incl. Tax)" min_bill_turnover_desc: "once turnover exceeds %{mbt_amount}" + business_model_configuration: "Business model configuration" say_no: "No" say_yes: "Yes" then: then diff --git a/config/routes.rb b/config/routes.rb index f1b96de181..cd7c7780c4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -55,6 +55,7 @@ Openfoodnetwork::Application.routes.draw do member do get :shop + get :relatives end end get '/:id/shop', to: 'enterprises#shop', as: 'enterprise_shop' diff --git a/lib/open_food_network/orders_and_fulfillments_report.rb b/lib/open_food_network/orders_and_fulfillments_report.rb index 089838a1b1..b4778a12aa 100644 --- a/lib/open_food_network/orders_and_fulfillments_report.rb +++ b/lib/open_food_network/orders_and_fulfillments_report.rb @@ -216,6 +216,7 @@ module OpenFoodNetwork proc { |line_items| "" }, proc { |line_items| "" }, proc { |line_items| "" }, + proc { |line_items| line_items.all? { |li| li.order.paid? } ? "Yes" : "No" }, proc { |line_items| line_items.first.order.shipping_method.andand.name }, proc { |line_items| rsa.call(line_items) ? 'Y' : 'N' }, diff --git a/spec/features/admin/business_model_configuration_spec.rb b/spec/features/admin/business_model_configuration_spec.rb index 05d5367437..ab5e4fd2e7 100644 --- a/spec/features/admin/business_model_configuration_spec.rb +++ b/spec/features/admin/business_model_configuration_spec.rb @@ -42,6 +42,7 @@ feature 'Business Model Configuration' do click_button "Update" + expect(page).to have_content "Business model configuration has been successfully updated!" expect(Spree::Config.account_invoices_monthly_fixed).to eq 10 expect(Spree::Config.account_invoices_monthly_rate).to eq 0.05 expect(Spree::Config.account_invoices_monthly_cap).to eq 30 diff --git a/spec/features/admin/customers_spec.rb b/spec/features/admin/customers_spec.rb index 61caae507f..89893860df 100644 --- a/spec/features/admin/customers_spec.rb +++ b/spec/features/admin/customers_spec.rb @@ -60,7 +60,7 @@ feature 'Customers' do create(:order, customer: customer1) expect{ within "tr#c_#{customer1.id}" do - find("a.delete-customer").click + find("a.delete-customer").trigger('click') end expect(page).to have_selector "#info-dialog .text", text: "Delete failed: customer has associated orders" click_button "OK" @@ -115,7 +115,7 @@ feature 'Customers' do expect(customer1.tag_list).to eq [] end - it "prevents duplicate codes from being saved" do + it "prevents duplicate codes from being saved", retry: 3 do select2_select managed_distributor1.name, from: "shop_id" within "tr#c_#{customer1.id}" do diff --git a/spec/features/consumer/groups_spec.rb b/spec/features/consumer/groups_spec.rb index e51ac39ea2..5ea96ac17e 100644 --- a/spec/features/consumer/groups_spec.rb +++ b/spec/features/consumer/groups_spec.rb @@ -12,8 +12,8 @@ feature 'Groups', js: true do page.should have_content group.name end - it "renders enterprise modals for groups" do - visit groups_path - page.should have_content group.name + it "searches by URL" do + visit groups_path(anchor: "/?query=xyzzy") + expect(page).to have_content "No groups found" end end diff --git a/spec/features/consumer/producers_spec.rb b/spec/features/consumer/producers_spec.rb index 634350af18..5c89a23529 100644 --- a/spec/features/consumer/producers_spec.rb +++ b/spec/features/consumer/producers_spec.rb @@ -27,45 +27,54 @@ feature %q{ producer1.set_producer_property 'Local', 'Victoria' producer2.set_producer_property 'Fair Trade', 'FT123' - - visit producers_path end - it "filters by taxon" do - toggle_filters - - toggle_filter 'Vegetables' - - page.should_not have_content producer1.name - page.should have_content producer2.name - - toggle_filter 'Vegetables' - toggle_filter 'Fruit' - - page.should have_content producer1.name - page.should_not have_content producer2.name + it "searches by URL" do + visit producers_path(anchor: "/?query=xyzzy") + expect(page).to have_content "Sorry, no results found for xyzzy" end - it "shows all producers with expandable details" do - page.should have_content producer1.name - expand_active_table_node producer1.name + context "on the producers page" do + before do + visit producers_path + end - # -- Taxons - page.should have_content 'Fruit' + it "filters by taxon" do + toggle_filters - # -- Properties - page.should have_content 'Organic' # Product property - page.should have_content 'Local' # Producer property - end + toggle_filter 'Vegetables' - it "doesn't show invisible producers" do - page.should_not have_content invisible_producer.name - end + page.should_not have_content producer1.name + page.should have_content producer2.name - it "links to places to buy produce" do - expand_active_table_node producer1.name - page.should have_link shop.name + toggle_filter 'Vegetables' + toggle_filter 'Fruit' + + page.should have_content producer1.name + page.should_not have_content producer2.name + end + + it "shows all producers with expandable details" do + page.should have_content producer1.name + expand_active_table_node producer1.name + + # -- Taxons + page.should have_content 'Fruit' + + # -- Properties + page.should have_content 'Organic' # Product property + page.should have_content 'Local' # Producer property + end + + it "doesn't show invisible producers" do + page.should_not have_content invisible_producer.name + end + + it "links to places to buy produce" do + expand_active_table_node producer1.name + page.should have_link shop.name + end end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 7dc9f474da..b449b4d083 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -34,7 +34,7 @@ feature "As a consumer I want to shop with a distributor", js: true do it "shows the producers for a distributor" do exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) - exchange.variants << variant + add_variant_to_order_cycle(exchange, variant) visit shop_path find("#tab_producers a").click @@ -68,7 +68,7 @@ feature "As a consumer I want to shop with a distributor", js: true do it "shows products after selecting an order cycle" do variant.update_attribute(:display_name, "kitten") variant.update_attribute(:display_as, "rabbit") - exchange1.variants << variant ## add product to exchange + add_variant_to_order_cycle(exchange1, variant) visit shop_path page.should_not have_content product.name Spree::Order.last.order_cycle.should == nil @@ -89,8 +89,8 @@ feature "As a consumer I want to shop with a distributor", js: true do it "shows the correct fees after selecting and changing an order cycle" do enterprise_fee = create(:enterprise_fee, amount: 1001) exchange2.enterprise_fees << enterprise_fee - exchange2.variants << variant - exchange1.variants << variant + add_variant_to_order_cycle(exchange2, variant) + add_variant_to_order_cycle(exchange1, variant) # -- Selecting an order cycle visit shop_path @@ -116,8 +116,8 @@ feature "As a consumer I want to shop with a distributor", js: true do describe "declining to clear the cart" do before do - exchange2.variants << variant - exchange1.variants << variant + add_variant_to_order_cycle(exchange2, variant) + add_variant_to_order_cycle(exchange1, variant) visit shop_path select "turtles", from: "order_cycle_id" @@ -147,9 +147,9 @@ feature "As a consumer I want to shop with a distributor", js: true do before do exchange.update_attribute :pickup_time, "frogs" - exchange.variants << variant - exchange.variants << variant1 - exchange.variants << variant2 + add_variant_to_order_cycle(exchange, variant) + add_variant_to_order_cycle(exchange, variant1) + add_variant_to_order_cycle(exchange, variant2) order.order_cycle = oc1 end diff --git a/spec/features/consumer/shops_spec.rb b/spec/features/consumer/shops_spec.rb index 8b1a709462..0c5d3d7e09 100644 --- a/spec/features/consumer/shops_spec.rb +++ b/spec/features/consumer/shops_spec.rb @@ -14,56 +14,70 @@ feature 'Shops', js: true do before do producer.set_producer_property 'Organic', 'NASAA 12345' - visit shops_path end - it "shows hubs" do - page.should have_content distributor.name - expand_active_table_node distributor.name - page.should have_content "OUR PRODUCERS" + it "searches by URL" do + visit shops_path(anchor: "/?query=xyzzy") + expect(page).to have_content "Sorry, no results found for xyzzy" end - it "does not show invisible hubs" do - page.should_not have_content invisible_distributor.name - end - it "should not show hubs that are not in an order cycle" do - create(:simple_product, distributors: [d1, d2]) - visit shops_path - page.should have_no_selector 'hub.inactive' - page.should have_no_selector 'hub', text: d2.name - end + context "on the shops path" do + before do + visit shops_path + end - it "should show closed shops after clicking the button" do - create(:simple_product, distributors: [d1, d2]) - visit shops_path - click_link_and_ensure("Show closed shops", -> { page.has_selector? 'hub.inactive' }) - page.should have_selector 'hub.inactive', text: d2.name - end - - it "should link to the hub page" do - follow_active_table_node distributor.name - expect(page).to have_current_path enterprise_shop_path(distributor) - end - - describe "hub producer modal" do - let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) } - let!(:taxon) { create(:taxon, name: 'Fruit') } - let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } - - it "should show hub producer modals" do + it "shows hubs" do + page.should have_content distributor.name expand_active_table_node distributor.name - expect(page).to have_content producer.name - open_enterprise_modal producer - modal_should_be_open_for producer + page.should have_content "OUR PRODUCERS" + end - within ".reveal-modal" do - expect(page).to have_content 'Fruit' # Taxon - expect(page).to have_content 'Organic' # Producer property + it "does not show invisible hubs" do + page.should_not have_content invisible_distributor.name + end + + it "should not show hubs that are not in an order cycle" do + create(:simple_product, distributors: [d1, d2]) + visit shops_path + page.should have_no_selector 'hub.inactive' + page.should have_no_selector 'hub', text: d2.name + end + + it "should show closed shops after clicking the button" do + create(:simple_product, distributors: [d1, d2]) + visit shops_path + click_link_and_ensure("Show closed shops", -> { page.has_selector? 'hub.inactive' }) + page.should have_selector 'hub.inactive', text: d2.name + end + + it "should link to the hub page" do + follow_active_table_node distributor.name + expect(page).to have_current_path enterprise_shop_path(distributor) + end + + describe "hub producer modal" do + let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) } + let!(:taxon) { create(:taxon, name: 'Fruit') } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } + + it "should show hub producer modals" do + expand_active_table_node distributor.name + expect(page).to have_content producer.name + open_enterprise_modal producer + modal_should_be_open_for producer + + within ".reveal-modal" do + expect(page).to have_content 'Fruit' # Taxon + expect(page).to have_content 'Organic' # Producer property + end end end end + + private + def click_link_and_ensure(link_text, check) # Buttons appear to be unresponsive for a while, so keep clicking them until content appears using_wait_time 0.5 do diff --git a/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb b/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb index 55c8bd1197..f37dcefa02 100644 --- a/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb +++ b/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb @@ -93,5 +93,24 @@ module OpenFoodNetwork end end end + + describe "columns are aligned" do + let(:d1) { create(:distributor_enterprise) } + let(:oc1) { create(:simple_order_cycle) } + let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) } + let(:li1) { build(:line_item) } + let(:user) { create(:admin_user)} + + before { o1.line_items << li1 } + + it 'has aligned columsn' do + report_types = ["", "order_cycle_supplier_totals", "order_cycle_supplier_totals_by_distributor", "order_cycle_distributor_totals_by_supplier", "order_cycle_customer_totals"] + + report_types.each do |report_type| + report = OrdersAndFulfillmentsReport.new user, report_type: report_type + report.header.size.should == report.columns.size + end + end + end end end diff --git a/spec/support/request/shop_workflow.rb b/spec/support/request/shop_workflow.rb index 279ead2630..01c24bee05 100644 --- a/spec/support/request/shop_workflow.rb +++ b/spec/support/request/shop_workflow.rb @@ -29,10 +29,24 @@ module ShopWorkflow end def add_variant_to_order_cycle(exchange, variant) + ensure_supplier_exchange(exchange, variant.product.supplier) exchange.variants << variant end def set_order_cycle(order, order_cycle) order.update_attribute(:order_cycle, order_cycle) end + + private + + # An order cycle needs an incoming exchange for a supplier + # before having its products. Otherwise the data will be inconsistent and + # and not all needed enterprises are loaded into the shop page. + def ensure_supplier_exchange(exchange, supplier) + oc = exchange.order_cycle + if oc.exchanges.from_enterprise(supplier).incoming.empty? + create(:exchange, order_cycle: oc, incoming: true, + sender: supplier, receiver: oc.coordinator) + end + end end