mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge branch 'master' into product-amount-units
Conflicts: app/assets/javascripts/admin/bulk_product_update.js.coffee spec/spec_helper.rb
This commit is contained in:
6
Gemfile
6
Gemfile
@@ -85,4 +85,10 @@ end
|
||||
group :development do
|
||||
gem 'pry-debugger'
|
||||
gem 'debugger-linecache'
|
||||
gem 'guard'
|
||||
gem 'guard-livereload'
|
||||
gem 'rack-livereload'
|
||||
gem 'guard-rails'
|
||||
gem 'guard-zeus'
|
||||
gem 'guard-rspec'
|
||||
end
|
||||
|
||||
42
Gemfile.lock
42
Gemfile.lock
@@ -182,6 +182,8 @@ GEM
|
||||
rack-test (>= 0.5.4)
|
||||
selenium-webdriver (~> 2.0)
|
||||
xpath (~> 1.0.0)
|
||||
celluloid (0.15.2)
|
||||
timers (~> 1.1.0)
|
||||
childprocess (0.3.9)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
chunky_png (1.2.8)
|
||||
@@ -230,6 +232,9 @@ GEM
|
||||
devise-encryptable (0.1.2)
|
||||
devise (>= 2.1.0)
|
||||
diff-lcs (1.2.4)
|
||||
em-websocket (0.5.0)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.5.3)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.3)
|
||||
excon (0.25.3)
|
||||
@@ -260,6 +265,24 @@ GEM
|
||||
fssm (0.2.10)
|
||||
geocoder (1.1.8)
|
||||
gmaps4rails (1.5.6)
|
||||
guard (2.2.4)
|
||||
formatador (>= 0.2.4)
|
||||
listen (~> 2.1)
|
||||
lumberjack (~> 1.0)
|
||||
pry (>= 0.9.12)
|
||||
thor (>= 0.18.1)
|
||||
guard-livereload (2.0.1)
|
||||
em-websocket (~> 0.5)
|
||||
guard (~> 2.0)
|
||||
multi_json (~> 1.8)
|
||||
guard-rails (0.4.7)
|
||||
guard (>= 0.2.2)
|
||||
guard-rspec (4.0.4)
|
||||
guard (>= 2.1.1)
|
||||
rspec (~> 2.14)
|
||||
guard-zeus (0.0.1)
|
||||
guard
|
||||
zeus
|
||||
haml (4.0.4)
|
||||
tilt
|
||||
highline (1.6.18)
|
||||
@@ -287,6 +310,11 @@ GEM
|
||||
letter_opener (1.0.0)
|
||||
launchy (>= 2.0.4)
|
||||
libv8 (3.16.14.3)
|
||||
listen (2.2.0)
|
||||
celluloid (>= 0.15.2)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
lumberjack (1.0.4)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
@@ -332,6 +360,8 @@ GEM
|
||||
rack (1.4.5)
|
||||
rack-cache (1.2)
|
||||
rack (>= 0.4)
|
||||
rack-livereload (0.3.15)
|
||||
rack
|
||||
rack-ssl (1.3.3)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
@@ -357,6 +387,9 @@ GEM
|
||||
actionpack (~> 3.0)
|
||||
activerecord (~> 3.0)
|
||||
polyamorous (~> 0.5.0)
|
||||
rb-fsevent (0.9.3)
|
||||
rb-inotify (0.9.2)
|
||||
ffi (>= 0.5.0)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
ref (1.0.5)
|
||||
@@ -420,6 +453,7 @@ GEM
|
||||
thor (0.18.1)
|
||||
tilt (1.4.1)
|
||||
timecop (0.6.2.2)
|
||||
timers (1.1.0)
|
||||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
@@ -453,6 +487,8 @@ GEM
|
||||
websocket (1.0.7)
|
||||
xpath (1.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
zeus (0.13.3)
|
||||
method_source (>= 0.6.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -476,6 +512,11 @@ DEPENDENCIES
|
||||
faker
|
||||
geocoder
|
||||
gmaps4rails
|
||||
guard
|
||||
guard-livereload
|
||||
guard-rails
|
||||
guard-rspec
|
||||
guard-zeus
|
||||
haml
|
||||
jquery-rails
|
||||
json_spec
|
||||
@@ -488,6 +529,7 @@ DEPENDENCIES
|
||||
poltergeist
|
||||
pry-debugger
|
||||
rabl
|
||||
rack-livereload
|
||||
rack-ssl
|
||||
rails (= 3.2.14)
|
||||
representative_view
|
||||
|
||||
50
Guardfile
Normal file
50
Guardfile
Normal file
@@ -0,0 +1,50 @@
|
||||
# A sample Guardfile
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
|
||||
guard 'livereload' do
|
||||
watch(%r{app/views/.+\.(erb|haml|slim)$})
|
||||
watch(%r{app/helpers/.+\.rb})
|
||||
watch(%r{public/.+\.(css|js|html)})
|
||||
#watch(%r{config/locales/.+\.yml})
|
||||
# Rails Assets Pipeline
|
||||
watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" }
|
||||
end
|
||||
|
||||
|
||||
#guard 'rails' do
|
||||
#watch('Gemfile.lock')
|
||||
#watch(%r{^(config|lib)/.*})
|
||||
#end
|
||||
|
||||
|
||||
#guard 'zeus' do
|
||||
## uses the .rspec file
|
||||
## --colour --fail-fast --format documentation --tag ~slow
|
||||
#watch(%r{^spec/.+_spec\.rb$})
|
||||
#watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
#watch(%r{^app/(.+)\.haml$}) { |m| "spec/#{m[1]}.haml_spec.rb" }
|
||||
#watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
#watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/requests/#{m[1]}_spec.rb"] }
|
||||
#end
|
||||
|
||||
#guard :rspec do
|
||||
#watch(%r{^spec/.+_spec\.rb$})
|
||||
#watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
#watch('spec/spec_helper.rb') { "spec" }
|
||||
|
||||
## Rails example
|
||||
#watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
#watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
||||
#watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
||||
#watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
||||
#watch('config/routes.rb') { "spec/routing" }
|
||||
#watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
||||
|
||||
## Capybara features specs
|
||||
#watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
||||
|
||||
## Turnip features and steps
|
||||
#watch(%r{^spec/acceptance/(.+)\.feature$})
|
||||
#watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
||||
#end
|
||||
|
||||
BIN
app/assets/images/logo.png
Normal file
BIN
app/assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
app/assets/images/matte.png
Normal file
BIN
app/assets/images/matte.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -65,6 +65,7 @@ productsApp.directive "ofnToggleVariants", ->
|
||||
element.addClass "icon-chevron-down"
|
||||
|
||||
|
||||
|
||||
productsApp.directive "ofnToggleColumn", ->
|
||||
link: (scope, element, attrs) ->
|
||||
element.addClass "unselected" unless scope.column.visible
|
||||
@@ -77,24 +78,6 @@ productsApp.directive "ofnToggleColumn", ->
|
||||
scope.column.visible = true
|
||||
element.removeClass "unselected"
|
||||
|
||||
|
||||
productsApp.directive "ofnToggleColumnList", [
|
||||
"$compile"
|
||||
($compile) ->
|
||||
return link: (scope, element, attrs) ->
|
||||
dialogDiv = element.next()
|
||||
element.on "click", ->
|
||||
pos = element.position()
|
||||
height = element.outerHeight()
|
||||
dialogDiv.css(
|
||||
position: "absolute"
|
||||
top: (pos.top + height) + "px"
|
||||
left: pos.left + "px"
|
||||
).toggle()
|
||||
|
||||
]
|
||||
|
||||
|
||||
productsApp.directive "datetimepicker", [
|
||||
"$parse"
|
||||
($parse) ->
|
||||
@@ -126,7 +109,7 @@ productsApp.controller "AdminBulkProductsCtrl", [
|
||||
unit: {name: "Unit", visible: true}
|
||||
price: {name: "Price", visible: true}
|
||||
on_hand: {name: "On Hand", visible: true}
|
||||
available_on: {name: "Available On", visible: true}
|
||||
available_on: {name: "Available On", visible: false}
|
||||
|
||||
$scope.variant_unit_options = [
|
||||
["Weight (g)", "weight_1"],
|
||||
@@ -138,6 +121,39 @@ productsApp.controller "AdminBulkProductsCtrl", [
|
||||
["Items", "items"]
|
||||
]
|
||||
|
||||
$scope.filterableColumns = [
|
||||
{ name: "Supplier", db_column: "supplier_name" },
|
||||
{ name: "Name", db_column: "name" }
|
||||
]
|
||||
|
||||
$scope.filterTypes = [
|
||||
{ name: "Equals", predicate: "eq" },
|
||||
{ name: "Contains", predicate: "cont" }
|
||||
]
|
||||
|
||||
$scope.optionTabs =
|
||||
filters: { title: "Filter Products", visible: false }
|
||||
column_toggle: { title: "Toggle Columns", visible: false }
|
||||
|
||||
$scope.visibleTab = { title: "Lala" }
|
||||
$scope.perPage = 25
|
||||
$scope.currentPage = 1
|
||||
$scope.products = []
|
||||
$scope.filteredProducts = []
|
||||
$scope.currentFilters = []
|
||||
$scope.totalCount = -> $scope.filteredProducts.length
|
||||
$scope.totalPages = -> Math.ceil($scope.totalCount()/$scope.perPage)
|
||||
$scope.firstVisibleProduct = -> ($scope.currentPage-1)*$scope.perPage+1
|
||||
$scope.lastVisibleProduct = -> Math.min($scope.totalCount(),$scope.currentPage*$scope.perPage)
|
||||
$scope.setPage = (page) -> $scope.currentPage = page
|
||||
$scope.minPage = -> Math.max(1,Math.min($scope.totalPages()-4,$scope.currentPage-2))
|
||||
$scope.maxPage = -> Math.min($scope.totalPages(),Math.max(5,$scope.currentPage+2))
|
||||
|
||||
$scope.$watch ->
|
||||
$scope.totalPages()
|
||||
, (newVal, oldVal) ->
|
||||
$scope.currentPage = Math.max $scope.totalPages(), 1 if newVal != oldVal && $scope.totalPages() < $scope.currentPage
|
||||
|
||||
$scope.initialise = (spree_api_key) ->
|
||||
authorise_api_reponse = ""
|
||||
dataFetcher("/api/users/authorise_api?token=" + spree_api_key).then (data) ->
|
||||
@@ -148,14 +164,23 @@ productsApp.controller "AdminBulkProductsCtrl", [
|
||||
dataFetcher("/api/enterprises/managed?template=bulk_index&q[is_primary_producer_eq]=true").then (data) ->
|
||||
$scope.suppliers = data
|
||||
# Need to have suppliers before we get products so we can match suppliers to product.supplier
|
||||
dataFetcher("/api/products/managed?template=bulk_index;page=1;per_page=500").then (data) ->
|
||||
$scope.resetProducts data
|
||||
$scope.fetchProducts()
|
||||
else if authorise_api_reponse.hasOwnProperty("error")
|
||||
$scope.api_error_msg = authorise_api_reponse("error")
|
||||
else
|
||||
api_error_msg = "You don't have an API key yet. An attempt was made to generate one, but you are currently not authorised, please contact your site administrator for access."
|
||||
|
||||
|
||||
$scope.fetchProducts = -> # WARNING: returns a promise
|
||||
$scope.loading = true
|
||||
queryString = $scope.currentFilters.reduce (qs,f) ->
|
||||
return qs + "q[#{f.property.db_column}_#{f.predicate.predicate}]=#{f.value};"
|
||||
, ""
|
||||
return dataFetcher("/api/products/managed?template=bulk_index;page=1;per_page=500;#{queryString}").then (data) ->
|
||||
$scope.resetProducts data
|
||||
$scope.loading = false
|
||||
|
||||
|
||||
$scope.resetProducts = (data) ->
|
||||
$scope.products = data
|
||||
$scope.dirtyProducts = {}
|
||||
@@ -214,6 +239,23 @@ productsApp.controller "AdminBulkProductsCtrl", [
|
||||
onHand = "error"
|
||||
onHand
|
||||
|
||||
$scope.shiftTab = (tab) ->
|
||||
$scope.visibleTab.visible = false unless $scope.visibleTab == tab
|
||||
tab.visible = !tab.visible
|
||||
$scope.visibleTab = tab
|
||||
|
||||
$scope.addFilter = (filter) ->
|
||||
if $scope.filterableColumns.indexOf(filter.property) >= 0
|
||||
if $scope.filterTypes.indexOf(filter.predicate) >= 0
|
||||
$scope.currentFilters.push filter
|
||||
$scope.fetchProducts()
|
||||
|
||||
$scope.removeFilter = (filter) ->
|
||||
index = $scope.currentFilters.indexOf(filter)
|
||||
if index != -1
|
||||
$scope.currentFilters.splice index, 1
|
||||
$scope.fetchProducts()
|
||||
|
||||
|
||||
$scope.editWarn = (product, variant) ->
|
||||
if ($scope.dirtyProductCount() > 0 and confirm("Unsaved changes will be lost. Continue anyway?")) or ($scope.dirtyProductCount() == 0)
|
||||
@@ -265,11 +307,19 @@ productsApp.controller "AdminBulkProductsCtrl", [
|
||||
$http(
|
||||
method: "POST"
|
||||
url: "/admin/products/bulk_update"
|
||||
data: productsToSubmit
|
||||
data:
|
||||
products: productsToSubmit
|
||||
filters: $scope.currentFilters
|
||||
).success((data) ->
|
||||
# TODO: remove this check altogether, need to write controller tests if we want to test this behaviour properly
|
||||
# Note: Rob implemented subset(), which is a simpler alternative to productsWithoutDerivedAttributes(). However, it
|
||||
# conflicted with some changes I made before merging my work, so for now I've reverted to the old way of
|
||||
# doing things. TODO: Review together and decide on strategy here. -- Rohan, 14-1-2014
|
||||
#if subset($scope.productsWithoutDerivedAttributes(), data)
|
||||
|
||||
if angular.toJson($scope.productsWithoutDerivedAttributes($scope.products)) == angular.toJson($scope.productsWithoutDerivedAttributes(data))
|
||||
$scope.resetProducts data
|
||||
$scope.displaySuccess()
|
||||
$timeout -> $scope.displaySuccess()
|
||||
else
|
||||
$scope.displayFailure "Product lists do not match."
|
||||
).error (data, status) ->
|
||||
@@ -285,8 +335,10 @@ productsApp.controller "AdminBulkProductsCtrl", [
|
||||
$scope.packProduct product
|
||||
|
||||
productsToSubmit = filterSubmitProducts($scope.dirtyProducts)
|
||||
$scope.updateProducts productsToSubmit
|
||||
|
||||
if productsToSubmit.length > 0
|
||||
$scope.updateProducts productsToSubmit # Don't submit an empty list
|
||||
else
|
||||
$scope.setMessage $scope.updateStatusMessage, "No changes to update.", color: "grey", 3000
|
||||
|
||||
$scope.packProduct = (product) ->
|
||||
if product.variant_unit_with_scale
|
||||
@@ -394,6 +446,10 @@ productsApp.factory "dataFetcher", [
|
||||
deferred.promise
|
||||
]
|
||||
|
||||
productsApp.filter "rangeArray", ->
|
||||
return (input,start,end) ->
|
||||
input.push(i) for i in [start..end]
|
||||
input
|
||||
|
||||
filterSubmitProducts = (productsToFilter) ->
|
||||
filteredProducts = []
|
||||
@@ -477,3 +533,11 @@ toObjectWithIDKeys = (array) ->
|
||||
object[array[i].id].variants = toObjectWithIDKeys(array[i].variants) if array[i].hasOwnProperty("variants") and array[i].variants instanceof Array
|
||||
|
||||
object
|
||||
|
||||
subset = (bigArray,smallArray) ->
|
||||
if smallArray instanceof Array && bigArray instanceof Array && smallArray.length > 0
|
||||
for item in smallArray
|
||||
return false if angular.toJson(bigArray).indexOf(angular.toJson(item)) == -1
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
||||
14
app/assets/javascripts/darkswarm/all.js.coffee
Normal file
14
app/assets/javascripts/darkswarm/all.js.coffee
Normal file
@@ -0,0 +1,14 @@
|
||||
#= require jquery
|
||||
#= require jquery_ujs
|
||||
#= require jquery-ui
|
||||
#= require spin
|
||||
#= require ../shared/angular
|
||||
#= require ../shared/angular-resource
|
||||
#= require foundation
|
||||
#= require ./shop
|
||||
#= require_tree .
|
||||
|
||||
$ ->
|
||||
$(document).foundation()
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Shop.controller "OrderCycleCtrl", ($scope, $rootScope, OrderCycle) ->
|
||||
$scope.order_cycle = OrderCycle.order_cycle
|
||||
$scope.changeOrderCycle = ->
|
||||
OrderCycle.push_order_cycle()
|
||||
@@ -0,0 +1,13 @@
|
||||
angular.module("Shop").controller "ProductsCtrl", ($scope, $rootScope, Product) ->
|
||||
$scope.data = Product.data
|
||||
Product.update()
|
||||
|
||||
|
||||
#$scope.order_cycle = OrderCycle.order_cycle
|
||||
#$scope.updateProducts = ->
|
||||
#$scope.products = Product.all()
|
||||
#$scope.$watch "order_cycle.order_cycle_id", $scope.updateProducts
|
||||
#$scope.updateProducts()
|
||||
|
||||
|
||||
|
||||
20
app/assets/javascripts/darkswarm/overrides.js.coffee
Normal file
20
app/assets/javascripts/darkswarm/overrides.js.coffee
Normal file
@@ -0,0 +1,20 @@
|
||||
Foundation.libs.section.toggle_active = (e)->
|
||||
$this = $(this)
|
||||
self = Foundation.libs.section
|
||||
region = $this.parent()
|
||||
content = $this.siblings(self.settings.content_selector)
|
||||
section = region.parent()
|
||||
settings = $.extend({}, self.settings, self.data_options(section))
|
||||
prev_active_region = section.children(self.settings.region_selector).filter("." + self.settings.active_class)
|
||||
|
||||
#for anchors inside [data-section-title]
|
||||
e.preventDefault() if not settings.deep_linking and content.length > 0
|
||||
e.stopPropagation() #do not catch same click again on parent
|
||||
unless region.hasClass(self.settings.active_class)
|
||||
prev_active_region.removeClass self.settings.active_class
|
||||
region.addClass self.settings.active_class
|
||||
#force resize for better performance (do not wait timer)
|
||||
self.resize region.find(self.settings.section_selector).not("[" + self.settings.resized_data_attr + "]"), true
|
||||
else if not settings.one_up# and (self.small(section) or self.is_vertical_nav(section) or self.is_horizontal_nav(section) or self.is_accordion(section))
|
||||
region.removeClass self.settings.active_class
|
||||
settings.callback section
|
||||
@@ -0,0 +1,7 @@
|
||||
Shop.factory 'OrderCycle', ($resource, Product, orderCycleData) ->
|
||||
class OrderCycle
|
||||
@order_cycle = orderCycleData || {orders_close_at: ""}
|
||||
@push_order_cycle: ->
|
||||
new $resource("/shop/order_cycle").save {order_cycle_id: @order_cycle.order_cycle_id}, (order_data)->
|
||||
OrderCycle.order_cycle.orders_close_at = order_data.orders_close_at
|
||||
Product.update()
|
||||
11
app/assets/javascripts/darkswarm/services/product.js.coffee
Normal file
11
app/assets/javascripts/darkswarm/services/product.js.coffee
Normal file
@@ -0,0 +1,11 @@
|
||||
Shop.factory 'Product', ($resource) ->
|
||||
new class Product
|
||||
data: {
|
||||
products: null
|
||||
}
|
||||
update: ->
|
||||
@data.products = $resource("/shop/products").query =>
|
||||
#console.log @products
|
||||
@data
|
||||
all: ->
|
||||
@data.products || @update()
|
||||
14
app/assets/javascripts/darkswarm/shop.js.coffee
Normal file
14
app/assets/javascripts/darkswarm/shop.js.coffee
Normal file
@@ -0,0 +1,14 @@
|
||||
window.Shop = angular.module("Shop", ["ngResource", "filters"]).config ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
|
||||
|
||||
#angular.module('Shop', ['filters'])
|
||||
|
||||
angular.module("filters", []).filter "truncate", ->
|
||||
(text, length, end) ->
|
||||
text = text || ""
|
||||
length = 10 if isNaN(length)
|
||||
end = "..." if end is `undefined`
|
||||
if text.length <= length or text.length - end.length <= length
|
||||
text
|
||||
else
|
||||
String(text).substring(0, length - end.length) + end
|
||||
3
app/assets/javascripts/distributors.js.coffee
Normal file
3
app/assets/javascripts/distributors.js.coffee
Normal file
@@ -0,0 +1,3 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -2,6 +2,59 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.pagination {
|
||||
div.pagenav {
|
||||
margin: 0px;
|
||||
span.first, span.prev, span.next, span.last {
|
||||
padding: 5px 0px;
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.pagination_info {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
div.applied_filter {
|
||||
margin-bottom: 5px;
|
||||
border: solid 2px #5498da;
|
||||
padding: 5px 0px;
|
||||
border-radius: 5px;
|
||||
div.four.columns {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
div.option_tabs {
|
||||
div.applied_filters, div.filters, div.column_toggle {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
div.option_tab_titles {
|
||||
h6 {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #cee1f4;
|
||||
padding: 3px;
|
||||
text-align: center;
|
||||
color: darken(#cee1f4, 3);
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
h6.selected {
|
||||
border: 1px solid #5498da;
|
||||
color: #5498da;
|
||||
}
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
tbody.odd {
|
||||
tr.product { td { background-color: white; } }
|
||||
tr.variant.odd { td { background-color: lighten(#eff5fc, 3); } }
|
||||
@@ -27,11 +80,18 @@ li.column-list-item {
|
||||
border-radius: 3px;
|
||||
padding: 2px 20px;
|
||||
margin: 2px 1px;
|
||||
border: 2px solid darken(#5498da, 3);
|
||||
border: 2px solid #5498da;
|
||||
background-color: #5498da;
|
||||
color: white;
|
||||
font-size: 100%;
|
||||
cursor: default;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
li.column-list-item.unselected {
|
||||
@@ -42,10 +102,7 @@ li.column-list-item.unselected {
|
||||
}
|
||||
|
||||
ul.column-list {
|
||||
padding: 5px 8px;
|
||||
border-radius: 3px;
|
||||
border: solid 1px darkgray;
|
||||
list-style:none
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
table#listing_products.bulk {
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
*= require_self
|
||||
*= require_tree .
|
||||
*/
|
||||
*/
|
||||
|
||||
10
app/assets/stylesheets/darkswarm/all.scss
Normal file
10
app/assets/stylesheets/darkswarm/all.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
||||
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
|
||||
*= require_self
|
||||
|
||||
*= require foundation
|
||||
*= require_tree .
|
||||
*/
|
||||
12
app/assets/stylesheets/darkswarm/footer.sass
Normal file
12
app/assets/stylesheets/darkswarm/footer.sass
Normal file
@@ -0,0 +1,12 @@
|
||||
@import variables
|
||||
|
||||
#footer
|
||||
padding: 74px 0px
|
||||
background: $fawn
|
||||
margin-top: 85px
|
||||
|
||||
#copyright
|
||||
clear: both
|
||||
img
|
||||
display: block
|
||||
margin: 0px auto 8px
|
||||
3
app/assets/stylesheets/darkswarm/header.css.sass
Normal file
3
app/assets/stylesheets/darkswarm/header.css.sass
Normal file
@@ -0,0 +1,3 @@
|
||||
/*body { background: #ff0000; }*/
|
||||
nav.top-bar
|
||||
margin-bottom: 0px
|
||||
8
app/assets/stylesheets/darkswarm/mixins.sass
Normal file
8
app/assets/stylesheets/darkswarm/mixins.sass
Normal file
@@ -0,0 +1,8 @@
|
||||
@import typography
|
||||
|
||||
@mixin big-input
|
||||
border: 1px solid #999
|
||||
font-size: 18px
|
||||
@extend .avenir
|
||||
padding: 18px
|
||||
margin-bottom: 1.25em
|
||||
2
app/assets/stylesheets/darkswarm/overrides.css.sass
Normal file
2
app/assets/stylesheets/darkswarm/overrides.css.sass
Normal file
@@ -0,0 +1,2 @@
|
||||
.row
|
||||
max-width: 74em
|
||||
104
app/assets/stylesheets/darkswarm/shop.css.sass
Normal file
104
app/assets/stylesheets/darkswarm/shop.css.sass
Normal file
@@ -0,0 +1,104 @@
|
||||
@import mixins
|
||||
@import variables
|
||||
|
||||
product
|
||||
display: block
|
||||
|
||||
shop
|
||||
#search
|
||||
font-size: 2em
|
||||
@include big-input
|
||||
color: #666
|
||||
display: block
|
||||
navigation
|
||||
display: block
|
||||
background: $fawn
|
||||
distributor.details
|
||||
box-sizing: border-box
|
||||
display: block
|
||||
height: 150px
|
||||
padding: 40px 0px 0px
|
||||
select
|
||||
width: 200px
|
||||
position: relative
|
||||
img
|
||||
display: block
|
||||
height: 100px
|
||||
width: 100px
|
||||
margin-right: 12px
|
||||
location
|
||||
font-family: "AvenirBla_IE", "AvenirBla"
|
||||
padding-right: 16px
|
||||
ordercycle
|
||||
display: block
|
||||
position: absolute
|
||||
right: 0px
|
||||
top: 40px
|
||||
form.custom
|
||||
width: 400px
|
||||
text-align: right
|
||||
& > strong
|
||||
line-height: 2.5
|
||||
font-size: 1.29em
|
||||
padding-right: 14px
|
||||
.custom.dropdown
|
||||
width: 280px
|
||||
display: inline-block
|
||||
background: transparent
|
||||
border-width: 2px
|
||||
border-color: #666666
|
||||
font-size: 1.28em
|
||||
margin-bottom: 0
|
||||
closing
|
||||
font-size: 0.875em
|
||||
display: block
|
||||
float: right
|
||||
padding-top: 14px
|
||||
|
||||
tabs
|
||||
background: url("/assets/matte.png") top left repeat
|
||||
display: block
|
||||
.section-container.auto
|
||||
margin-bottom: 0
|
||||
& > section
|
||||
transition:height 0s linear 0.5s
|
||||
& > .content
|
||||
background: none
|
||||
border: none
|
||||
& > .title, &.active > .title
|
||||
text-transform: uppercase
|
||||
line-height: 50px
|
||||
border: none
|
||||
&, &:hover
|
||||
background: none
|
||||
a
|
||||
padding: 0px 2.2em
|
||||
|
||||
products
|
||||
display: block
|
||||
padding-top: 36px
|
||||
table
|
||||
width: 100%
|
||||
border-collapse: collapse
|
||||
border: none
|
||||
th
|
||||
line-height: 50px
|
||||
.notes
|
||||
max-width: 300px
|
||||
td, th
|
||||
background: #fff
|
||||
border: 1px solid #cccccc
|
||||
border-left: 0px
|
||||
border-right: 0px
|
||||
td
|
||||
padding: 20px 0px
|
||||
input[type=number]
|
||||
width: 60px
|
||||
margin: 0px
|
||||
|
||||
|
||||
|
||||
display: block
|
||||
float: right
|
||||
padding-top: 14px
|
||||
|
||||
36
app/assets/stylesheets/darkswarm/typography.css.sass
Normal file
36
app/assets/stylesheets/darkswarm/typography.css.sass
Normal file
@@ -0,0 +1,36 @@
|
||||
@font-face
|
||||
font-family: 'AvenirBla_IE'
|
||||
src: url("/AveniBla.eot") format("opentype")
|
||||
|
||||
@font-face
|
||||
font-family: 'AvenirBla'
|
||||
src: url("/AvenirLTStd-Black.otf") format("opentype")
|
||||
|
||||
@font-face
|
||||
font-family: 'AvenirMed_IE'
|
||||
src: url("/AveniMed.eot") format("opentype")
|
||||
|
||||
@font-face
|
||||
font-family: 'AvenirMed'
|
||||
src: url("/AvenirLTStd-Medium.otf") format("opentype")
|
||||
|
||||
//body
|
||||
//font-family: "AvenirBla_IE", "AvenirBla"
|
||||
|
||||
h1, h2, h3, h4, h5, h6, .avenir
|
||||
color: #333333
|
||||
font-family: "AvenirBla_IE", "AvenirBla"
|
||||
padding: 0px
|
||||
|
||||
strong.avenir
|
||||
font-weight: normal // Avenir is basically bold anyway
|
||||
|
||||
td
|
||||
font-family: "helvetica"
|
||||
|
||||
// These selectors match the default Foundation selectors
|
||||
// For clean overriden magic
|
||||
table tr th, table tr td
|
||||
color: #333333
|
||||
table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td
|
||||
color: #333333
|
||||
1
app/assets/stylesheets/darkswarm/variables.css.sass
Normal file
1
app/assets/stylesheets/darkswarm/variables.css.sass
Normal file
@@ -0,0 +1 @@
|
||||
$fawn: #f6efe5
|
||||
3
app/assets/stylesheets/distributors.css.scss
Normal file
3
app/assets/stylesheets/distributors.css.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
// Place all the styles related to the distributors controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
5
app/controllers/darkswarm_controller.rb
Normal file
5
app/controllers/darkswarm_controller.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class DarkswarmController < ApplicationController
|
||||
def index
|
||||
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,6 @@ class EnterprisesController < BaseController
|
||||
helper Spree::ProductsHelper
|
||||
include OrderCyclesHelper
|
||||
|
||||
|
||||
def index
|
||||
@enterprises = Enterprise.all
|
||||
end
|
||||
@@ -66,7 +65,6 @@ class EnterprisesController < BaseController
|
||||
|
||||
order_cycle_options = OrderCycle.active.with_distributor(distributor)
|
||||
order.order_cycle = order_cycle_options.first if order_cycle_options.count == 1
|
||||
|
||||
order.save!
|
||||
|
||||
redirect_to main_app.enterprise_path(distributor)
|
||||
|
||||
48
app/controllers/shop_controller.rb
Normal file
48
app/controllers/shop_controller.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
class ShopController < BaseController
|
||||
layout "darkswarm"
|
||||
|
||||
before_filter :set_distributor
|
||||
before_filter :set_order_cycles
|
||||
|
||||
def show
|
||||
# All suppliers of all our products
|
||||
@producers = Exchange.where(receiver_id: @distributor.id).map{ |ex| ex.variants.map {|v| v.product.supplier }}.flatten.uniq
|
||||
end
|
||||
|
||||
def products
|
||||
unless @products = current_order_cycle.andand.products_distributed_by(@distributor)
|
||||
render json: "", status: 404
|
||||
end
|
||||
end
|
||||
|
||||
def order_cycle
|
||||
if request.post?
|
||||
if oc = OrderCycle.with_distributor(@distributor).active.find_by_id(params[:order_cycle_id])
|
||||
current_order(true).set_order_cycle! oc
|
||||
render partial: "shop/order_cycle"
|
||||
else
|
||||
render status: 404, json: ""
|
||||
end
|
||||
else
|
||||
render partial: "shop/order_cycle"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_distributor
|
||||
|
||||
unless @distributor = current_distributor
|
||||
redirect_to root_path
|
||||
end
|
||||
end
|
||||
|
||||
def set_order_cycles
|
||||
@order_cycles = OrderCycle.with_distributor(@distributor).active
|
||||
|
||||
# And default to the only order cycle if there's only the one
|
||||
if @order_cycles.count == 1
|
||||
current_order(true).set_order_cycle! @order_cycles.first
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -11,11 +11,16 @@ Spree::Admin::ProductsController.class_eval do
|
||||
end
|
||||
|
||||
def bulk_update
|
||||
collection_hash = Hash[params[:_json].each_with_index.map { |p,i| [i,p] }]
|
||||
collection_hash = Hash[params[:products].each_with_index.map { |p,i| [i,p] }]
|
||||
product_set = Spree::ProductSet.new({:collection_attributes => collection_hash})
|
||||
|
||||
params[:filters] ||= {}
|
||||
bulk_index_query = params[:filters].reduce("") do |string, filter|
|
||||
"#{string}q[#{filter[:property][:db_column]}_#{filter[:predicate][:predicate]}]=#{filter[:value]};"
|
||||
end
|
||||
|
||||
if product_set.save
|
||||
redirect_to "/api/products/managed?template=bulk_index&page=1&per_page=500"
|
||||
redirect_to "/api/products/managed?template=bulk_index;page=1;per_page=500;#{bulk_index_query}"
|
||||
else
|
||||
render :nothing => true, :status => 418
|
||||
end
|
||||
|
||||
@@ -5,14 +5,13 @@ Spree::OrdersController.class_eval do
|
||||
before_filter :update_distribution, :only => :update
|
||||
before_filter :filter_order_params, :only => :update
|
||||
|
||||
# Patch Orders#populate to provide distributor_id and order_cycle_id to OrderPopulator
|
||||
# Patch Orders#populate to populate multi_cart (if enabled)
|
||||
def populate
|
||||
if OpenFoodNetwork::FeatureToggle.enabled? :multi_cart
|
||||
populate_cart params.slice(:products, :variants, :quantity, :distributor_id, :order_cycle_id)
|
||||
end
|
||||
|
||||
populator = Spree::OrderPopulator.new(current_order(true), current_currency)
|
||||
if populator.populate(params.slice(:products, :variants, :quantity, :distributor_id, :order_cycle_id))
|
||||
if populator.populate(params.slice(:products, :variants, :quantity))
|
||||
fire_event('spree.cart.add')
|
||||
fire_event('spree.order.contents_changed')
|
||||
respond_with(@order) do |format|
|
||||
|
||||
@@ -2,4 +2,4 @@ class SuburbsController < ActionController::Base
|
||||
def index
|
||||
@suburbs = Suburb.matching(params[:term]).order(:name).limit(8)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,8 +54,8 @@ module OrderCyclesHelper
|
||||
OpenFoodNetwork::FeatureToggle.enabled? :order_cycles
|
||||
end
|
||||
|
||||
def pickup_time
|
||||
current_order_cycle.exchanges.to_enterprises(current_distributor).outgoing.first.pickup_time
|
||||
def pickup_time(order_cycle = current_order_cycle)
|
||||
order_cycle.exchanges.to_enterprises(current_distributor).outgoing.first.pickup_time
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
10
app/helpers/shop_helper.rb
Normal file
10
app/helpers/shop_helper.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
module ShopHelper
|
||||
def order_cycles_name_and_pickup_times(order_cycles)
|
||||
order_cycles.map do |oc|
|
||||
[
|
||||
pickup_time(oc),
|
||||
oc.id
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,7 +8,7 @@ class Cart < ActiveRecord::Base
|
||||
order = create_or_find_order_for_distributor distributor, order_cycle, currency
|
||||
|
||||
@populator = Spree::OrderPopulator.new(order, currency)
|
||||
@populator.populate({ :variants => { variant_id => quantity }, :distributor_id => distributor.id, :order_cycle_id => order_cycle })
|
||||
@populator.populate({ :variants => { variant_id => quantity } })
|
||||
end
|
||||
|
||||
def create_or_find_order_for_distributor distributor, order_cycle, currency
|
||||
@@ -17,6 +17,7 @@ class Cart < ActiveRecord::Base
|
||||
order_for_distributor = Spree::Order.create(:currency => currency, :distributor => distributor)
|
||||
order_for_distributor.distributor = distributor
|
||||
order_for_distributor.order_cycle = order_cycle
|
||||
order_for_distributor.save!
|
||||
orders << order_for_distributor
|
||||
end
|
||||
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
Spree::OrderPopulator.class_eval do
|
||||
def populate_with_distribution_validation(from_hash)
|
||||
@distributor, @order_cycle = load_distributor_and_order_cycle(from_hash)
|
||||
@distributor, @order_cycle = distributor_and_order_cycle
|
||||
|
||||
# Refactor: We may not need this validation - we can't change distribution here, so
|
||||
# this validation probably can't fail
|
||||
if !distribution_can_supply_products_in_cart(@distributor, @order_cycle)
|
||||
errors.add(:base, "That distributor or order cycle can't supply all the products in your cart. Please choose another.")
|
||||
end
|
||||
|
||||
# Set order distributor and order cycle
|
||||
@orig_distributor, @orig_order_cycle = orig_distributor_and_order_cycle
|
||||
cart_distribution_set = false
|
||||
if valid?
|
||||
set_cart_distributor_and_order_cycle @distributor, @order_cycle
|
||||
cart_distribution_set = true
|
||||
end
|
||||
|
||||
populate_without_distribution_validation(from_hash) if valid?
|
||||
|
||||
# Undo distribution setting if validation failed when adding a product
|
||||
if !valid? && cart_distribution_set
|
||||
set_cart_distributor_and_order_cycle @orig_distributor, @orig_order_cycle
|
||||
end
|
||||
|
||||
valid?
|
||||
end
|
||||
alias_method_chain :populate, :distribution_validation
|
||||
@@ -32,7 +21,7 @@ Spree::OrderPopulator.class_eval do
|
||||
variant = Spree::Variant.find(variant_id)
|
||||
if quantity > 0
|
||||
if check_stock_levels(variant, quantity) &&
|
||||
check_distribution_provided_for(variant) &&
|
||||
check_order_cycle_provided_for(variant) &&
|
||||
check_variant_available_under_distribution(variant)
|
||||
|
||||
@order.add_variant(variant, quantity, currency)
|
||||
@@ -43,44 +32,18 @@ Spree::OrderPopulator.class_eval do
|
||||
|
||||
private
|
||||
|
||||
def orig_distributor_and_order_cycle
|
||||
def distributor_and_order_cycle
|
||||
[@order.distributor, @order.order_cycle]
|
||||
end
|
||||
|
||||
|
||||
def load_distributor_and_order_cycle(from_hash)
|
||||
distributor = from_hash[:distributor_id].present? ?
|
||||
Enterprise.is_distributor.find(from_hash[:distributor_id]) : nil
|
||||
order_cycle = from_hash[:order_cycle_id].present? ?
|
||||
OrderCycle.find(from_hash[:order_cycle_id]) : nil
|
||||
|
||||
[distributor, order_cycle]
|
||||
end
|
||||
|
||||
def set_cart_distributor_and_order_cycle(distributor, order_cycle)
|
||||
# Using @order.reload or not performing any reload causes totals fields (ie. item_total)
|
||||
# to be set to zero
|
||||
@order = Spree::Order.find @order.id
|
||||
|
||||
@order.set_distribution! distributor, order_cycle
|
||||
end
|
||||
|
||||
def distribution_can_supply_products_in_cart(distributor, order_cycle)
|
||||
DistributionChangeValidator.new(@order).can_change_to_distribution?(distributor, order_cycle)
|
||||
end
|
||||
|
||||
def check_distribution_provided_for(variant)
|
||||
distribution_provided = distribution_provided_for variant
|
||||
|
||||
unless distribution_provided
|
||||
if order_cycle_required_for variant
|
||||
errors.add(:base, "Please choose a distributor and order cycle for this order.")
|
||||
else
|
||||
errors.add(:base, "Please choose a distributor for this order.")
|
||||
end
|
||||
end
|
||||
|
||||
distribution_provided
|
||||
def check_order_cycle_provided_for(variant)
|
||||
order_cycle_provided = (!order_cycle_required_for(variant) || @order_cycle.present?)
|
||||
errors.add(:base, "Please choose an order cycle for this order.") unless order_cycle_provided
|
||||
order_cycle_provided
|
||||
end
|
||||
|
||||
def check_variant_available_under_distribution(variant)
|
||||
@@ -92,10 +55,6 @@ Spree::OrderPopulator.class_eval do
|
||||
end
|
||||
end
|
||||
|
||||
def distribution_provided_for(variant)
|
||||
@distributor.present? && (!order_cycle_required_for(variant) || @order_cycle.present?)
|
||||
end
|
||||
|
||||
def order_cycle_required_for(variant)
|
||||
variant.product.product_distributions.empty?
|
||||
end
|
||||
|
||||
1
app/views/darkswarm/index.html.haml
Normal file
1
app/views/darkswarm/index.html.haml
Normal file
@@ -0,0 +1 @@
|
||||
TESTING
|
||||
@@ -15,4 +15,4 @@
|
||||
%label
|
||||
= f.check_box :remember_me
|
||||
= f.label :remember_me, t(:remember_me)
|
||||
%p= f.submit t(:login), :class => 'button primary', :tabindex => 3, :id => "login_spree_user_remember_me"
|
||||
%p= f.submit t(:login), :class => 'button primary', :tabindex => 3, :id => "login_spree_user_remember_me"
|
||||
|
||||
26
app/views/layouts/darkswarm.html.haml
Normal file
26
app/views/layouts/darkswarm.html.haml
Normal file
@@ -0,0 +1,26 @@
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%meta{charset: 'utf-8'}/
|
||||
%meta{name: 'viewport', content: "width=device-width,initial-scale=1.0"}/
|
||||
|
||||
%title= content_for?(:title) ? yield(:title) : 'Welcome to Open Food Network'
|
||||
= favicon_link_tag "favicon.png"
|
||||
|
||||
= stylesheet_link_tag "darkswarm/all"
|
||||
= javascript_include_tag "darkswarm/all"
|
||||
= render "layouts/bugherd_script"
|
||||
= csrf_meta_tags
|
||||
|
||||
%body.off-canvas
|
||||
= render partial: "shared/menu"
|
||||
|
||||
%section{ role: "main" }
|
||||
= yield
|
||||
|
||||
%section#sidebar{ role: "complementary" }
|
||||
= render partial: "shared/login_panel"
|
||||
= yield :sidebar
|
||||
|
||||
= yield :scripts
|
||||
|
||||
13
app/views/products/_list.html.haml
Normal file
13
app/views/products/_list.html.haml
Normal file
@@ -0,0 +1,13 @@
|
||||
%table#product-list
|
||||
%thead
|
||||
%th Item
|
||||
%th Description
|
||||
%th Variant
|
||||
%th Quantity
|
||||
%th Available?
|
||||
%th Price
|
||||
|
||||
- list.each do |product|
|
||||
%tr
|
||||
%td= product.name
|
||||
%td= product.description
|
||||
3
app/views/shared/_copyright.html.haml
Normal file
3
app/views/shared/_copyright.html.haml
Normal file
@@ -0,0 +1,3 @@
|
||||
#copyright.text-center
|
||||
%img.copyright{src: "/assets/logo.png", alt: "Open Food Network"}
|
||||
© Copyright 2013 Open Food Foundation
|
||||
12
app/views/shared/_login.html.haml
Normal file
12
app/views/shared/_login.html.haml
Normal file
@@ -0,0 +1,12 @@
|
||||
- if spree_current_user.nil?
|
||||
%li#login-link= link_to "Login", "#sidebar", id: "sidebarLoginButton", class: "sidebar-button"
|
||||
%li#login-name.hide
|
||||
%li.divider
|
||||
%li#sign-up-link= link_to "Sign Up", "#sidebar", id: "sidebarSignUpButton", class: "sidebar-button"
|
||||
%li#sign-out-link.hide= link_to "Sign Out", "/logout"
|
||||
- else
|
||||
%li#login-link.hide= link_to "Login", "#sidebar", id: "sidebarLoginButton", class: "sidebar-button"
|
||||
%li#login-name= link_to "#{spree_current_user.email}", "#"
|
||||
%li.divider
|
||||
%li#sign-up-link.hide= link_to "Sign Up", "#"
|
||||
%li#sign-out-link= link_to "Sign Out", "/logout"
|
||||
5
app/views/shared/_login_panel.html.haml
Normal file
5
app/views/shared/_login_panel.html.haml
Normal file
@@ -0,0 +1,5 @@
|
||||
.login-panel
|
||||
#login-content.hide
|
||||
= render "home/login"
|
||||
#sign-up-content.hide
|
||||
= render "home/signup"
|
||||
11
app/views/shared/_menu.html.haml
Normal file
11
app/views/shared/_menu.html.haml
Normal file
@@ -0,0 +1,11 @@
|
||||
%nav.top-bar
|
||||
%section.top-bar-section
|
||||
%ul.left
|
||||
%li= link_to image_tag("ofn_logo_small.png"), new_landing_page_path
|
||||
%li.divider
|
||||
= render partial: "shared/login"
|
||||
|
||||
%ul.right
|
||||
%li= link_to "Distributors", "#", :data => { "reveal-id" => "become-distributor" }
|
||||
%li.divider
|
||||
%li= link_to "Farmers", "#", :data => { "reveal-id" => "become-farmer" }
|
||||
3
app/views/shop/_about_us.html.haml
Normal file
3
app/views/shop/_about_us.html.haml
Normal file
@@ -0,0 +1,3 @@
|
||||
.about.right.text-right.small-2.large-3.columns
|
||||
%h3 About Us
|
||||
%p= @distributor.long_description.andand.html_safe
|
||||
8
app/views/shop/_contact_us.html.haml
Normal file
8
app/views/shop/_contact_us.html.haml
Normal file
@@ -0,0 +1,8 @@
|
||||
.contact.small-2.large-3.columns
|
||||
%h3 Contact
|
||||
%ul
|
||||
%li= @distributor.email
|
||||
%li= @distributor.website
|
||||
= @distributor.address.address1
|
||||
= @distributor.address.address2
|
||||
= @distributor.address.city
|
||||
4
app/views/shop/_last_order_cycle.html.haml
Normal file
4
app/views/shop/_last_order_cycle.html.haml
Normal file
@@ -0,0 +1,4 @@
|
||||
- if most_recently_closed = OrderCycle.most_recently_closed_for(@distributor)
|
||||
The last cycle closed
|
||||
= distance_of_time_in_words_to_now most_recently_closed.orders_close_at
|
||||
ago
|
||||
3
app/views/shop/_next_order_cycle.html.haml
Normal file
3
app/views/shop/_next_order_cycle.html.haml
Normal file
@@ -0,0 +1,3 @@
|
||||
- if next_oc = OrderCycle.first_opening_for(@distributor)
|
||||
The next cycle opens in
|
||||
= distance_of_time_in_words_to_now next_oc.orders_open_at
|
||||
3
app/views/shop/_order_cycle.rabl
Normal file
3
app/views/shop/_order_cycle.rabl
Normal file
@@ -0,0 +1,3 @@
|
||||
object current_order_cycle
|
||||
attributes :orders_close_at
|
||||
attribute :id => :order_cycle_id
|
||||
28
app/views/shop/_order_cycles.html.haml
Normal file
28
app/views/shop/_order_cycles.html.haml
Normal file
@@ -0,0 +1,28 @@
|
||||
%ordercycle{"ng-controller" => "OrderCycleCtrl"}
|
||||
|
||||
:javascript
|
||||
angular.module('Shop').value('orderCycleData', #{render "shop/order_cycle"})
|
||||
|
||||
|
||||
- if @order_cycles.empty?
|
||||
Orders are currently closed for this hub
|
||||
%p
|
||||
Please contact your hub directly to see if they accept late orders,
|
||||
or wait until the next cycle opens.
|
||||
|
||||
= render partial: "shop/next_order_cycle"
|
||||
= render partial: "shop/last_order_cycle"
|
||||
|
||||
- else
|
||||
%form.custom
|
||||
%strong.avenir Ready for
|
||||
%select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id",
|
||||
"ng-change" => "changeOrderCycle()",
|
||||
"ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}"}
|
||||
|
||||
|
||||
%closing
|
||||
-#%img{src: "/icon/goes/here"}
|
||||
Orders close
|
||||
%strong {{ order_cycle.orders_close_at | date:'EEEE MM'}}
|
||||
|
||||
48
app/views/shop/_products.html.haml
Normal file
48
app/views/shop/_products.html.haml
Normal file
@@ -0,0 +1,48 @@
|
||||
%products{"ng-controller" => "ProductsCtrl"}
|
||||
= form_for :order, :url => populate_orders_path, html: {:class => "custom"} do
|
||||
%input#search.text{"ng-model" => "query", placeholder: "Search"}
|
||||
%input.button.right{type: :submit, value: "Check Out"}
|
||||
%table
|
||||
%thead
|
||||
%th{colspan: 2} Item
|
||||
%th.notes Notes
|
||||
%th Variant
|
||||
%th QTY
|
||||
%th Bulk
|
||||
%th Price
|
||||
%tbody{"ng-repeat" => "product in data.products | filter:query"}
|
||||
%tr.product
|
||||
%td
|
||||
%img{"ng-src" => "{{ product.master.images[0].small_url }}"}
|
||||
{{product.master.images[0].alt}}
|
||||
%td
|
||||
%h5
|
||||
{{ product.name }}
|
||||
{{ product.supplier.name }}
|
||||
%td.notes {{ product.description | truncate:80 }}
|
||||
|
||||
%td {{ product.master.options_text }}
|
||||
%td
|
||||
%input{type: :number, value: 0, min: 0, name: "variants[{{product.master.id}}]"}
|
||||
%td.group_buy
|
||||
%span{"ng-show" => "product.group_buy"}
|
||||
Available
|
||||
%span{"ng-hide" => "product.group_buy"}
|
||||
Not available
|
||||
%td.price
|
||||
%small from
|
||||
${{ product.price }}
|
||||
%tr{"ng-repeat" => "variant in product.variants"}
|
||||
%td{colspan: 3}
|
||||
%td {{variant.options_text}}
|
||||
%td
|
||||
%input{type: :number, value: 0, min: 0, name: "variants[{{variant.id}}]"}
|
||||
%td.group_buy
|
||||
%span{"ng-show" => "product.group_buy"}
|
||||
Available
|
||||
%span{"ng-hide" => "product.group_buy"}
|
||||
Not available
|
||||
%td.price
|
||||
%small from ${{variant.price }}
|
||||
%input.button.right{type: :submit, value: "Check Out"}
|
||||
-#%pre {{ data.products | json }}
|
||||
25
app/views/shop/products.rabl
Normal file
25
app/views/shop/products.rabl
Normal file
@@ -0,0 +1,25 @@
|
||||
collection @products
|
||||
attributes :id, :name, :description, :price, :permalink
|
||||
|
||||
child :supplier do
|
||||
attributes :id, :name, :description
|
||||
end
|
||||
child :master => :master do
|
||||
attributes :id, :is_master, :count_on_hand, :options_text
|
||||
child :images => :images do
|
||||
attributes :id, :alt
|
||||
node do |img|
|
||||
{:small_url => img.attachment.url(:small, false)}
|
||||
end
|
||||
end
|
||||
end
|
||||
child :variants => :variants do |variant|
|
||||
attributes :id, :is_master, :count_on_hand, :options_text
|
||||
child :images => :images do
|
||||
attributes :id, :alt
|
||||
node do |img|
|
||||
{:small_url => img.attachment.url(:small, false)}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
50
app/views/shop/show.html.haml
Normal file
50
app/views/shop/show.html.haml
Normal file
@@ -0,0 +1,50 @@
|
||||
%shop{"ng-app" => "Shop"}
|
||||
%navigation
|
||||
%distributor.details.row
|
||||
%img.left{src: ""}
|
||||
%h4
|
||||
= @distributor.name
|
||||
%location= @distributor.address.city
|
||||
%small
|
||||
%a{href: "/"} Change location
|
||||
= render partial: "shop/order_cycles"
|
||||
|
||||
-#%description
|
||||
|
||||
%tabs
|
||||
.row
|
||||
.section-container.auto{"data-section" => "", "data-options" => "one_up: false"}
|
||||
%section
|
||||
%p.title.avenir{"data-section-title" => ""}
|
||||
%a{href: "#about"} About Us
|
||||
.content{"data-section-content" => ""}
|
||||
%p= @distributor.long_description.andand.html_safe
|
||||
|
||||
%section
|
||||
%p.title.avenir{"data-section-title" => ""}
|
||||
%a{href: "#producers"} Our Producers
|
||||
.content{"data-section-content" => ""}
|
||||
%ul
|
||||
- for producer in @producers
|
||||
%li= producer.name
|
||||
|
||||
%section
|
||||
%p.title.avenir{"data-section-title" => ""}
|
||||
%a{href: "#groups"} Our Groups
|
||||
.content{"data-section-content" => ""}
|
||||
%p Groups
|
||||
|
||||
%section
|
||||
%p.title.avenir{"data-section-title" => ""}
|
||||
%a{href: "#contact"} Contact
|
||||
.content{"data-section-content" => ""}
|
||||
%p Contact
|
||||
|
||||
|
||||
%products.row
|
||||
= render partial: "shop/products"
|
||||
#footer
|
||||
%section.row
|
||||
= render partial: "shop/contact_us"
|
||||
= render partial: "shop/about_us"
|
||||
= render partial: "shared/copyright"
|
||||
@@ -13,20 +13,79 @@
|
||||
|
||||
|
||||
|
||||
%div{ 'ng-app' => 'bulk_product_update', 'ng-controller' => 'AdminBulkProductsCtrl', 'ng-init' => "initialise('#{@spree_api_key}');" }
|
||||
%div{ 'ng-app' => 'bulk_product_update', 'ng-controller' => 'AdminBulkProductsCtrl', 'ng-init' => "initialise('#{@spree_api_key}');loading=true;" }
|
||||
%div{ 'ng-show' => '!spree_api_key_ok' }
|
||||
{{ api_error_msg }}
|
||||
%div
|
||||
%div.options
|
||||
Filter Results:
|
||||
%input.search{ 'ng-model' => 'query', :type => 'text', 'placeholder' => 'Search Value' }
|
||||
%input{ :type => 'button', :value => 'Toggle Columns', 'ofn-toggle-column-list' => true }
|
||||
%div{ :style => 'display: none;' }
|
||||
%ul.column-list{ style: 'border: 1px solid darkgray; background-color: white;' }
|
||||
%li.column-list-item{ 'ofn-toggle-column' => 'column', 'ng-repeat' => 'column in columns' }
|
||||
{{ column.name }}
|
||||
%br.clear
|
||||
%br.clear
|
||||
%div.option_tab_titles{ :class => "sixteen columns alpha" }
|
||||
%h6{ :class => "three columns alpha", 'ng-repeat' => "tab in optionTabs", "ng-click" => "shiftTab(tab)", "ng-class" => "tab.visible && 'selected' || !tab.visible && 'unselected'"}
|
||||
{{ tab.title }}
|
||||
%div.option_tabs{ :class => "sixteen columns alpha" }
|
||||
%div.filters{ :class => "sixteen columns alpha", "ng-show" => 'optionTabs.filters.visible' }
|
||||
%div{ :class => "four columns alpha" }
|
||||
Column:
|
||||
%br.clear
|
||||
%select.select2.fullwidth{ 'ng-model' => 'filterProperty', :name => "filter_property", 'ng-options' => 'fc.name for fc in filterableColumns' }
|
||||
%div{ :class => "four columns omega" }
|
||||
Filter Type:
|
||||
%br.clear
|
||||
%select.select2.fullwidth{ 'ng-model' => 'filterPredicate', :name => "filter_predicate", 'ng-options' => 'ft.name for ft in filterTypes' }
|
||||
%div{ :class => "six columns omega" }
|
||||
Value:
|
||||
%br.clear
|
||||
%input{ :class => "four columns alpha", 'ng-model' => 'filterValue', :name => "filter_value", :type => "text", 'placeholder' => 'Filter Value' }
|
||||
%div{ :class => "two columns omega" }
|
||||
|
||||
%input.fullwidth{ :name => "add_filter", :value => "Apply Filter", :type => "button", "ng-click" => "addFilter({property:filterProperty,predicate:filterPredicate,value:filterValue})" }
|
||||
%div.applied_filters{ :class => "sixteen columns alpha", "ng-show" => 'optionTabs.filters.visible && currentFilters.length > 0' }
|
||||
%div.applied_filter{ :class => "sixteen columns alpha", 'ng-repeat' => 'filter in currentFilters' }
|
||||
%div{ :class => "four columns alpha" }
|
||||
{{ filter.property.name }}
|
||||
%div{ :class => "four columns omega" }
|
||||
{{ filter.predicate.name }}
|
||||
%div{ :class => "six columns omega" }
|
||||
{{ filter.value }}
|
||||
%div{ :class => "two columns omega" }
|
||||
%a{ 'ng-click' => "removeFilter(filter)" } Remove Filter
|
||||
%div.column_toggle{ :class => "sixteen columns alpha", "ng-show" => 'optionTabs.column_toggle.visible' }
|
||||
%ul.column-list{ :class => "sixteen columns alpha" }
|
||||
%li.column-list-item{ :class => "three columns alpha", 'ofn-toggle-column' => 'column', 'ng-repeat' => 'column in columns' }
|
||||
{{ column.name }}
|
||||
%hr
|
||||
%div.loading{ 'ng-show' => 'loading' }
|
||||
%h4 Loading Products...
|
||||
%div{ 'ng-show' => '!loading && products.length == 0' }
|
||||
%h4{ :style => 'color:red;' } No matching products found.
|
||||
%div{ 'ng-show' => 'products.length == 500' }
|
||||
%h6 Search returned too many products to display (500+), please apply more search filters to reduce the number of matching products
|
||||
%div{ 'ng-hide' => 'loading || products.length == 500 || products.length == 0' }
|
||||
%div.quick_search{ :class => "five columns omega" }
|
||||
%input.search{ :class => "four columns alpha", 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' }
|
||||
%div.pagination{ :class => "seven columns omega" }
|
||||
%div.pagenav{ :class => "two columns alpha" }
|
||||
%span.first
|
||||
%a{ 'ng-click' => "currentPage = 1", 'ng-show' => "currentPage > 1" }
|
||||
« First
|
||||
%span.prev
|
||||
%a{ 'ng-click' => "currentPage = currentPage - 1", 'ng-show' => "currentPage > 1" }
|
||||
‹ Prev
|
||||
%div.pagenav{ :class => "columns omega" }
|
||||
%span.page{ 'ng-repeat' => "page in [] | rangeArray:minPage():maxPage()", 'ng-class' => "{current: currentPage==page}" }
|
||||
%a{ 'ng-click' => "setPage(page)" }
|
||||
{{page}}
|
||||
%span{ 'ng-show' => "maxPage() < totalPages()" } ...
|
||||
%div.pagenav{ :class => "two columns omega" }
|
||||
%span.next
|
||||
%a{ 'ng-click' => "currentPage = currentPage + 1", 'ng-show' => "currentPage < totalPages()" }
|
||||
Next ›
|
||||
%span.last
|
||||
%a{ 'ng-click' => "currentPage = totalPages()", 'ng-show' => "currentPage < totalPages()" }
|
||||
Last »
|
||||
%div.pagination_info{ :class => 'four columns alpha' }
|
||||
Show
|
||||
%select{ 'ng-model' => 'perPage', :name => 'perPage', 'ng-options' => 'pp for pp in [25,50,100,200]'}
|
||||
per page
|
||||
%br
|
||||
%span Displaying {{firstVisibleProduct()}}-{{lastVisibleProduct()}} of {{totalCount()}} products
|
||||
%table.index#listing_products.bulk
|
||||
%colgroup
|
||||
%col
|
||||
@@ -48,7 +107,7 @@
|
||||
%th{ 'ng-show' => 'columns.on_hand.visible' } On Hand
|
||||
%th{ 'ng-show' => 'columns.available_on.visible' } Av. On
|
||||
%th.actions
|
||||
%tbody{ 'ng-repeat' => 'product in products | filter:query', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" }
|
||||
%tbody{ 'ng-repeat' => 'product in filteredProducts = (products | filter:query)', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", 'ng-show' => "$index >= perPage*(currentPage-1) && $index < perPage*currentPage" }
|
||||
%tr.product
|
||||
%td.left-actions
|
||||
%a{ 'ofn-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' }
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
collection @products.order('id ASC')
|
||||
extends "spree/api/products/bulk_show"
|
||||
attributes :variant_unit, :variant_unit_scale, :variant_unit_name
|
||||
extends "spree/api/products/bulk_show"
|
||||
@@ -1,5 +1,5 @@
|
||||
object @product
|
||||
attributes :id, :name, :price, :on_hand
|
||||
attributes :id, :name, :price, :on_hand, :variant_unit, :variant_unit_scale, :variant_unit_name
|
||||
|
||||
node( :available_on ) { |p| p.available_on.blank? ? "" : p.available_on.strftime("%F %T") }
|
||||
node( :permalink_live ) { |p| p.permalink }
|
||||
|
||||
@@ -13,27 +13,6 @@
|
||||
- else
|
||||
= render 'add_to_cart_quantity_fields', product: @product
|
||||
|
||||
-#temporary hiding the options to choose order cycles and distributors
|
||||
%div.cleared.hide
|
||||
%br
|
||||
- available_distributors = available_distributors_for(order, @product)
|
||||
- available_order_cycles = available_order_cycles_for(order, @product)
|
||||
|
||||
- if available_distributors.length > 1 || order.andand.distributor.nil?
|
||||
= render 'add_to_cart_distributor_choice', distributor_collection: available_distributors
|
||||
- else
|
||||
- distributor = available_distributors.first
|
||||
- changing_distributor = distributor != order.andand.distributor
|
||||
= render 'add_to_cart_distributor_fixed', distributor: distributor, changing_distributor: changing_distributor
|
||||
|
||||
- if order_cycles_enabled?
|
||||
- if available_order_cycles.length > 1 || order.andand.order_cycle.nil?
|
||||
= render 'add_to_cart_order_cycle_choice', order_cycle_collection: available_order_cycles
|
||||
- else
|
||||
- order_cycle = available_order_cycles.first
|
||||
- changing_order_cycle = order_cycle != order.andand.order_cycle
|
||||
= render 'add_to_cart_order_cycle_fixed', order_cycle: order_cycle, changing_order_cycle: changing_order_cycle
|
||||
|
||||
%br
|
||||
= button_tag :class => 'large primary', :id => 'add-to-cart-button', :type => :submit do
|
||||
= t(:add_to_cart)
|
||||
|
||||
@@ -81,6 +81,7 @@ module Openfoodnetwork
|
||||
config.assets.initialize_on_precompile = true
|
||||
config.assets.precompile += ['store/all.css', 'store/all.js', 'store/shop_front.js']
|
||||
config.assets.precompile += ['admin/all.css', 'admin/restore_spree_from_cms.css', 'admin/*.js', 'admin/**/*.js']
|
||||
config.assets.precompile += ['darkswarm/all.css', 'darkswarm/all.js']
|
||||
config.assets.precompile += ['comfortable_mexican_sofa/*']
|
||||
config.assets.precompile += ['search/all.css', 'search/*.js']
|
||||
config.assets.precompile += ['shared/*']
|
||||
|
||||
@@ -11,11 +11,17 @@ module.exports = function(config) {
|
||||
|
||||
'app/assets/javascripts/admin/order_cycle.js.erb.coffee',
|
||||
'app/assets/javascripts/admin/bulk_product_update.js.coffee',
|
||||
'app/assets/javascripts/darkswarm/*.js*',
|
||||
'app/assets/javascripts/darkswarm/**/*.js*',
|
||||
|
||||
'spec/javascripts/unit/**/*.js*'
|
||||
],
|
||||
|
||||
exclude: ['**/.#*'],
|
||||
exclude: [
|
||||
'**/.#*',
|
||||
'app/assets/javascripts/darkswarm/all.js.coffee',
|
||||
'app/assets/javascripts/darkswarm/overrides.js.coffee'
|
||||
],
|
||||
|
||||
coffeePreprocessor: {
|
||||
options: {
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
Openfoodnetwork::Application.routes.draw do
|
||||
root :to => 'home#temp_landing_page'
|
||||
|
||||
|
||||
resource :shop, controller: :shop do
|
||||
get :products
|
||||
post :order_cycle
|
||||
get :order_cycle
|
||||
end
|
||||
|
||||
resources :enterprises do
|
||||
collection do
|
||||
get :suppliers
|
||||
@@ -37,6 +44,7 @@ Openfoodnetwork::Application.routes.draw do
|
||||
end
|
||||
|
||||
get "new_landing_page", :controller => 'home', :action => "new_landing_page"
|
||||
get "darkswarm", controller: :darkswarm, action: :index
|
||||
get "about_us", :controller => 'home', :action => "about_us"
|
||||
|
||||
namespace :open_food_network do
|
||||
|
||||
@@ -481,9 +481,9 @@ ActiveRecord::Schema.define(:version => 20140110040238) 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
|
||||
|
||||
|
||||
BIN
public/AveniBla.eot
Normal file
BIN
public/AveniBla.eot
Normal file
Binary file not shown.
BIN
public/AveniMed.eot
Normal file
BIN
public/AveniMed.eot
Normal file
Binary file not shown.
BIN
public/AvenirLTStd-Black.otf
Normal file
BIN
public/AvenirLTStd-Black.otf
Normal file
Binary file not shown.
BIN
public/AvenirLTStd-Medium.otf
Normal file
BIN
public/AvenirLTStd-Medium.otf
Normal file
Binary file not shown.
122
spec/controllers/shop_controller_spec.rb
Normal file
122
spec/controllers/shop_controller_spec.rb
Normal file
@@ -0,0 +1,122 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe ShopController do
|
||||
let(:d) { create(:distributor_enterprise) }
|
||||
|
||||
it "redirects to the home page if no distributor is selected" do
|
||||
spree_get :show
|
||||
response.should redirect_to root_path
|
||||
end
|
||||
|
||||
|
||||
describe "with a distributor in place" do
|
||||
before do
|
||||
controller.stub(:current_distributor).and_return d
|
||||
end
|
||||
|
||||
describe "Selecting order cycles" do
|
||||
it "should select an order cycle when only one order cycle is open" do
|
||||
oc1 = create(:order_cycle, distributors: [d])
|
||||
spree_get :show
|
||||
controller.current_order_cycle.should == oc1
|
||||
end
|
||||
|
||||
it "should not set an order cycle when multiple order cycles are open" do
|
||||
oc1 = create(:order_cycle, distributors: [d])
|
||||
oc2 = create(:order_cycle, distributors: [d])
|
||||
spree_get :show
|
||||
controller.current_order_cycle.should == nil
|
||||
end
|
||||
|
||||
it "should allow the user to post to select the current order cycle" do
|
||||
oc1 = create(:order_cycle, distributors: [d])
|
||||
oc2 = create(:order_cycle, distributors: [d])
|
||||
|
||||
spree_post :order_cycle, order_cycle_id: oc2.id
|
||||
response.should be_success
|
||||
controller.current_order_cycle.should == oc2
|
||||
end
|
||||
|
||||
context "RABL tests" do
|
||||
render_views
|
||||
it "should return the order cycle details when the oc is selected" do
|
||||
oc1 = create(:order_cycle, distributors: [d])
|
||||
oc2 = create(:order_cycle, distributors: [d])
|
||||
|
||||
spree_post :order_cycle, order_cycle_id: oc2.id
|
||||
response.should be_success
|
||||
response.body.should have_content oc2.id
|
||||
end
|
||||
|
||||
it "should return the current order cycle when hit with GET" do
|
||||
oc1 = create(:order_cycle, distributors: [d])
|
||||
controller.stub(:current_order_cycle).and_return oc1
|
||||
spree_get :order_cycle
|
||||
response.body.should have_content oc1.id
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
it "should not allow the user to select an invalid order cycle" do
|
||||
oc1 = create(:order_cycle, distributors: [d])
|
||||
oc2 = create(:order_cycle, distributors: [d])
|
||||
oc3 = create(:order_cycle, distributors: [create(:distributor_enterprise)])
|
||||
|
||||
spree_post :order_cycle, order_cycle_id: oc3.id
|
||||
response.status.should == 404
|
||||
controller.current_order_cycle.should == nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "producers/suppliers" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:product, supplier: supplier) }
|
||||
let(:order_cycle) { create(:order_cycle, distributors: [d], coordinator: create(:distributor_enterprise)) }
|
||||
|
||||
before do
|
||||
exchange = Exchange.find(order_cycle.exchanges.to_enterprises(d).outgoing.first.id)
|
||||
exchange.variants << product.master
|
||||
end
|
||||
|
||||
it "builds a list of producers/suppliers" do
|
||||
spree_get :show
|
||||
assigns[:producers].should == [supplier]
|
||||
end
|
||||
end
|
||||
|
||||
describe "returning products" do
|
||||
let(:product) { create(:product) }
|
||||
let(:order_cycle) { create(:order_cycle, distributors: [d], coordinator: create(:distributor_enterprise)) }
|
||||
|
||||
before do
|
||||
exchange = Exchange.find(order_cycle.exchanges.to_enterprises(d).outgoing.first.id)
|
||||
exchange.variants << product.master
|
||||
end
|
||||
|
||||
it "returns products via json" do
|
||||
controller.stub(:current_order_cycle).and_return order_cycle
|
||||
xhr :get, :products
|
||||
response.should be_success
|
||||
end
|
||||
|
||||
it "does not return products if no order_cycle is selected" do
|
||||
controller.stub(:current_order_cycle).and_return nil
|
||||
xhr :get, :products
|
||||
response.status.should == 404
|
||||
response.body.should be_empty
|
||||
end
|
||||
|
||||
context "RABL tests" do
|
||||
render_views
|
||||
it "only returns products for the current order cycle" do
|
||||
controller.stub(:current_order_cycle).and_return order_cycle
|
||||
xhr :get, :products
|
||||
response.body.should have_content product.name
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -168,7 +168,7 @@ describe Spree::Admin::ReportsController do
|
||||
|
||||
it "should build distributors for the current user" do
|
||||
spree_get :products_and_inventory
|
||||
assigns(:distributors).should == [d1, d2, d3]
|
||||
assigns(:distributors).sort.should == [d1, d2, d3].sort
|
||||
end
|
||||
|
||||
it "builds suppliers for the current user" do
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Spree::OrdersController do
|
||||
def current_user
|
||||
controller.current_user
|
||||
end
|
||||
|
||||
it "selects distributors" do
|
||||
d = create(:distributor_enterprise)
|
||||
p = create(:product, :distributors => [d])
|
||||
@@ -31,83 +27,18 @@ describe Spree::OrdersController do
|
||||
order.distributor.should be_nil
|
||||
end
|
||||
|
||||
|
||||
describe "adding a product to the cart with a distribution combination that can't service the existing cart" do
|
||||
before do
|
||||
@request.env["HTTP_REFERER"] = 'http://test.host/'
|
||||
end
|
||||
|
||||
it "errors when an invalid distributor is selected" do
|
||||
# Given a product and some distributors
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
p = create(:product, :price => 12.34)
|
||||
oc = create(:simple_order_cycle, :distributors => [d1], :variants => [p.master])
|
||||
|
||||
# When I attempt to add the product to the cart with an invalid distributor, it should not be added
|
||||
expect do
|
||||
spree_post :populate, variants: {p.master.id => 1}, distributor_id: d2.id, order_cycle_id: oc.id
|
||||
response.should redirect_to :back
|
||||
end.to change(self, :num_items_in_cart).by(0)
|
||||
|
||||
# And I should see an error
|
||||
flash[:error].should == "That product is not available from the chosen distributor or order cycle."
|
||||
end
|
||||
|
||||
it "errors when an invalid order cycle is selected" do
|
||||
# Given a product and some order cycles
|
||||
d = create(:distributor_enterprise)
|
||||
p = create(:product, :price => 12.34)
|
||||
oc1 = create(:simple_order_cycle, :distributors => [d], :variants => [p.master])
|
||||
oc2 = create(:simple_order_cycle, :distributors => [d], :variants => [])
|
||||
|
||||
# When I attempt to add the product to my cart with an invalid order cycle, it should not be added
|
||||
expect do
|
||||
spree_post :populate, variants: {p.master.id => 1}, distributor_id: d.id, order_cycle_id: oc2.id
|
||||
response.should redirect_to :back
|
||||
end.to change(self, :num_items_in_cart).by(0)
|
||||
|
||||
# And I should see an error
|
||||
flash[:error].should == "That product is not available from the chosen distributor or order cycle."
|
||||
end
|
||||
|
||||
it "errors when distribution is valid for the new product but does not cover the cart" do
|
||||
# Given two products with different distributors
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
p1 = create(:product, :price => 12.34)
|
||||
p2 = create(:product, :price => 23.45)
|
||||
oc1 = create(:simple_order_cycle, :distributors => [d1], :variants => [p1.master])
|
||||
oc2 = create(:simple_order_cycle, :distributors => [d2], :variants => [p2.master])
|
||||
|
||||
# When I add the first to my cart
|
||||
expect do
|
||||
spree_post :populate, variants: {p1.master.id => 1}, distributor_id: d1.id, order_cycle_id: oc1.id
|
||||
response.should redirect_to spree.cart_path
|
||||
end.to change(self, :num_items_in_cart).by(1)
|
||||
|
||||
# And I attempt to add the second, then the product should not be added to my cart
|
||||
expect do
|
||||
spree_post :populate, variants: {p2.master.id => 1}, distributor_id: d2.id, order_cycle_id: oc2.id
|
||||
response.should redirect_to :back
|
||||
end.to change(self, :num_items_in_cart).by(0)
|
||||
|
||||
# And I should see an error
|
||||
flash[:error].should == "That distributor or order cycle can't supply all the products in your cart. Please choose another."
|
||||
end
|
||||
end
|
||||
|
||||
context "adding a group buy product to the cart" do
|
||||
it "sets a variant attribute for the max quantity" do
|
||||
distributor_product = create(:distributor_enterprise)
|
||||
p = create(:product, :distributors => [distributor_product], :group_buy => true)
|
||||
|
||||
order = subject.current_order(true)
|
||||
order.stub(:distributor) { distributor_product }
|
||||
order.should_receive(:set_variant_attributes).with(p.master, {'max_quantity' => '3'})
|
||||
controller.stub(:current_order).and_return(order)
|
||||
|
||||
expect do
|
||||
spree_post :populate, :variants => {p.master.id => 1}, :variant_attributes => {p.master.id => {:max_quantity => 3}}, :distributor_id => distributor_product.id
|
||||
spree_post :populate, :variants => {p.master.id => 1}, :variant_attributes => {p.master.id => {:max_quantity => 3}}
|
||||
end.to change(Spree::LineItem, :count).by(1)
|
||||
end
|
||||
end
|
||||
@@ -141,6 +72,7 @@ describe Spree::OrdersController do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def num_items_in_cart
|
||||
|
||||
@@ -21,14 +21,45 @@ feature %q{
|
||||
login_to_admin_section
|
||||
end
|
||||
|
||||
it "displays a 'loading' splash for products" do
|
||||
FactoryGirl.create(:simple_product)
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_selector "div.loading", :text => "Loading Products..."
|
||||
end
|
||||
|
||||
it "displays a list of products" do
|
||||
p1 = FactoryGirl.create(:product)
|
||||
p2 = FactoryGirl.create(:product)
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_field "product_name", with: p1.name
|
||||
page.should have_field "product_name", with: p2.name
|
||||
page.should have_field "product_name", with: p1.name, :visible => true
|
||||
page.should have_field "product_name", with: p2.name, :visible => true
|
||||
end
|
||||
|
||||
it "displays a message when number of products is zero" do
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_text "No matching products found."
|
||||
end
|
||||
|
||||
pending "displays a message when number of products is too great" do
|
||||
501.times { FactoryGirl.create(:simple_product) }
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_text "Search returned too many products to display (500+), please apply more search filters to reduce the number of matching products"
|
||||
end
|
||||
|
||||
it "displays pagination information" do
|
||||
p1 = FactoryGirl.create(:product)
|
||||
p2 = FactoryGirl.create(:product)
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_text "Displaying 1-2 of 2 products"
|
||||
end
|
||||
|
||||
it "displays a select box for suppliers, with the appropriate supplier selected" do
|
||||
@@ -209,6 +240,9 @@ feature %q{
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
first("div.option_tab_titles h6", :text => "Toggle Columns").click
|
||||
first("li.column-list-item", text: "Available On").click
|
||||
|
||||
page.should have_field "product_name", with: p.name
|
||||
page.should have_select "supplier", selected: s1.name
|
||||
@@ -342,6 +376,53 @@ feature %q{
|
||||
page.find("span#update-status-message").should have_content "Update complete"
|
||||
end
|
||||
|
||||
scenario "updating a product after cloning a product" do
|
||||
FactoryGirl.create(:product, :name => "product 1")
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
first("a.clone-product").click
|
||||
|
||||
fill_in "product_name", :with => "new product name"
|
||||
|
||||
click_button 'Update'
|
||||
page.find("span#update-status-message").should have_content "Update complete"
|
||||
end
|
||||
|
||||
scenario "updating when no changes have been made" do
|
||||
Capybara.default_wait_time = 2
|
||||
FactoryGirl.create(:product, :name => "product 1")
|
||||
FactoryGirl.create(:product, :name => "product 2")
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
click_button 'Update'
|
||||
page.find("span#update-status-message").should have_content "No changes to update."
|
||||
Capybara.default_wait_time = 5
|
||||
end
|
||||
|
||||
scenario "updating when a filter has been applied" do
|
||||
p1 = FactoryGirl.create(:simple_product, :name => "product1")
|
||||
p2 = FactoryGirl.create(:simple_product, :name => "product2")
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
first("div.option_tab_titles h6", :text => "Filter Products").click
|
||||
|
||||
select "Name", :from => "filter_property"
|
||||
select "Contains", :from => "filter_predicate"
|
||||
fill_in "filter_value", :with => "1"
|
||||
click_button "Apply Filter"
|
||||
page.should_not have_field "product_name", with: p2.name
|
||||
fill_in "product_name", :with => "new product1"
|
||||
|
||||
click_on 'Update'
|
||||
page.find("span#update-status-message").should have_content "Update complete"
|
||||
end
|
||||
|
||||
scenario "updating a product when there are more products than the default API page size" do
|
||||
26.times { FactoryGirl.create(:simple_product) }
|
||||
login_to_admin_section
|
||||
@@ -447,7 +528,6 @@ feature %q{
|
||||
page.should have_selector "a.clone-product", :count => 3
|
||||
|
||||
first("a.clone-product").click
|
||||
|
||||
page.should have_selector "a.clone-product", :count => 4
|
||||
page.should have_field "product_name", with: "COPY OF #{p1.name}"
|
||||
page.should have_select "supplier", selected: "#{p1.supplier.name}"
|
||||
@@ -462,24 +542,61 @@ feature %q{
|
||||
end
|
||||
|
||||
describe "using the page" do
|
||||
describe "using column display toggle" do
|
||||
describe "using tabs to hide and display page controls" do
|
||||
it "shows a column display toggle button, which shows a list of columns when clicked" do
|
||||
FactoryGirl.create(:simple_product)
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_selector "div.column_toggle", :visible => false
|
||||
|
||||
page.should have_selector "div.option_tab_titles h6.unselected", :text => "Toggle Columns"
|
||||
first("div.option_tab_titles h6", :text => "Toggle Columns").click
|
||||
|
||||
page.should have_selector "div.option_tab_titles h6.selected", :text => "Toggle Columns"
|
||||
page.should have_selector "div.column_toggle", :visible => true
|
||||
page.should have_selector "li.column-list-item", text: "Available On"
|
||||
|
||||
page.should have_selector "div.filters", :visible => false
|
||||
|
||||
page.should have_selector "div.option_tab_titles h6.unselected", :text => "Filter Products"
|
||||
first("div.option_tab_titles h6", :text => "Filter Products").click
|
||||
|
||||
page.should have_selector "div.option_tab_titles h6.unselected", :text => "Toggle Columns"
|
||||
page.should have_selector "div.option_tab_titles h6.selected", :text => "Filter Products"
|
||||
page.should have_selector "div.filters", :visible => true
|
||||
page.should have_selector "li.column-list-item", text: "Available On"
|
||||
|
||||
first("div.option_tab_titles h6", :text => "Filter Products").click
|
||||
|
||||
page.should have_selector "div.option_tab_titles h6.unselected", :text => "Filter Products"
|
||||
page.should have_selector "div.option_tab_titles h6.unselected", :text => "Toggle Columns"
|
||||
page.should have_selector "div.filters", :visible => false
|
||||
page.should have_selector "div.column_toggle", :visible => false
|
||||
end
|
||||
end
|
||||
|
||||
describe "using column display toggle" do
|
||||
it "shows a column display toggle button, which shows a list of columns when clicked" do
|
||||
FactoryGirl.create(:simple_product)
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
first("div.option_tab_titles h6", :text => "Toggle Columns").click
|
||||
first("li.column-list-item", text: "Available On").click
|
||||
|
||||
page.should have_selector "th", :text => "NAME"
|
||||
page.should have_selector "th", :text => "SUPPLIER"
|
||||
page.should have_selector "th", :text => "PRICE"
|
||||
page.should have_selector "th", :text => "ON HAND"
|
||||
page.should have_selector "th", :text => "AV. ON"
|
||||
|
||||
page.should have_button "Toggle Columns"
|
||||
|
||||
click_button "Toggle Columns"
|
||||
page.should have_selector "div.option_tab_titles h6", :text => "Toggle Columns"
|
||||
|
||||
page.should have_selector "div ul.column-list li.column-list-item", text: "Supplier"
|
||||
all("div ul.column-list li.column-list-item").select{ |e| e.text == "Supplier" }.first.click
|
||||
first("li.column-list-item", text: "Supplier").click
|
||||
|
||||
page.should_not have_selector "th", :text => "SUPPLIER"
|
||||
page.should have_selector "th", :text => "NAME"
|
||||
@@ -488,6 +605,147 @@ feature %q{
|
||||
page.should have_selector "th", :text => "AV. ON"
|
||||
end
|
||||
end
|
||||
|
||||
describe "using pagination controls" do
|
||||
it "shows pagination controls" do
|
||||
27.times { FactoryGirl.create(:product) }
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_select 'perPage', :selected => '25'
|
||||
within '.pagination' do
|
||||
page.should have_text "1 2"
|
||||
page.should have_text "Next"
|
||||
page.should have_text "Last"
|
||||
end
|
||||
end
|
||||
|
||||
it "allows the number of visible products to be altered" do
|
||||
27.times { FactoryGirl.create(:product) }
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
select '25', :from => 'perPage'
|
||||
page.all("input[name='product_name']").select{ |e| e.visible? }.length.should == 25
|
||||
select '50', :from => 'perPage'
|
||||
page.all("input[name='product_name']").select{ |e| e.visible? }.length.should == 27
|
||||
end
|
||||
|
||||
it "displays the correct products when changing pages" do
|
||||
25.times { FactoryGirl.create(:product, :name => "page1product") }
|
||||
5.times { FactoryGirl.create(:product, :name => "page2product") }
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
select '25', :from => 'perPage'
|
||||
page.all("input[name='product_name']").select{ |e| e.visible? }.all?{ |e| e.value == "page1product" }.should == true
|
||||
click_link "2"
|
||||
page.all("input[name='product_name']").select{ |e| e.visible? }.all?{ |e| e.value == "page2product" }.should == true
|
||||
end
|
||||
|
||||
it "moves the user to the last available page when changing the number of pages in any way causes user to become orphaned" do
|
||||
50.times { FactoryGirl.create(:product) }
|
||||
FactoryGirl.create(:product, :name => "fancy_product_name")
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
select '25', :from => 'perPage'
|
||||
click_link "3"
|
||||
select '50', :from => 'perPage'
|
||||
page.first("div.pagenav span.page.current").should have_text "2"
|
||||
page.all("input[name='product_name']", :visible => true).length.should == 1
|
||||
|
||||
select '25', :from => 'perPage'
|
||||
fill_in "quick_filter", :with => "fancy_product_name"
|
||||
page.first("div.pagenav span.page.current").should have_text "1"
|
||||
page.all("input[name='product_name']", :visible => true).length.should == 1
|
||||
end
|
||||
|
||||
it "paginates the filtered product list rather than all products" do
|
||||
25.times { FactoryGirl.create(:product, :name => "product_name") }
|
||||
3.times { FactoryGirl.create(:product, :name => "test_product_name") }
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
select '25', :from => 'perPage'
|
||||
page.should have_text "1 2"
|
||||
fill_in "quick_filter", :with => "test_product_name"
|
||||
page.all("input[name='product_name']", :visible => true).length.should == 3
|
||||
page.all("input[name='product_name']", :visible => true).all?{ |e| e.value == "test_product_name" }.should == true
|
||||
page.should_not have_text "1 2"
|
||||
page.should have_text "1"
|
||||
end
|
||||
end
|
||||
|
||||
describe "using filtering controls" do
|
||||
it "displays basic filtering controls" do
|
||||
FactoryGirl.create(:simple_product)
|
||||
|
||||
login_to_admin_section
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_selector "div.option_tab_titles h6", :text => "Filter Products"
|
||||
first("div.option_tab_titles h6", :text => "Filter Products").click
|
||||
|
||||
page.should have_select "filter_property", :with_options => ["Supplier", "Name"]
|
||||
page.should have_select "filter_predicate", :with_options => ["Equals", "Contains"]
|
||||
page.should have_field "filter_value"
|
||||
end
|
||||
|
||||
describe "clicking the 'Apply Filter' Button" do
|
||||
before(:each) do
|
||||
FactoryGirl.create(:simple_product, :name => "Product1")
|
||||
FactoryGirl.create(:simple_product, :name => "Product2")
|
||||
|
||||
login_to_admin_section
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
first("div.option_tab_titles h6", :text => "Filter Products").click
|
||||
|
||||
select "Name", :from => "filter_property"
|
||||
select "Equals", :from => "filter_predicate"
|
||||
fill_in "filter_value", :with => "Product1"
|
||||
click_button "Apply Filter"
|
||||
end
|
||||
|
||||
it "adds a new filter to the list of applied filters" do
|
||||
page.should have_text "Name Equals Product1"
|
||||
end
|
||||
|
||||
it "displays the 'loading' splash" do
|
||||
page.should have_selector "div.loading", :text => "Loading Products..."
|
||||
end
|
||||
|
||||
it "loads appropriate products" do
|
||||
page.should have_field "product_name", :with => "Product1"
|
||||
page.should_not have_field "product_name", :with => "Product2"
|
||||
end
|
||||
|
||||
describe "clicking the 'Remove Filter' link" do
|
||||
before(:each) do
|
||||
click_link "Remove Filter"
|
||||
end
|
||||
|
||||
it "removes the filter from the list of applied filters" do
|
||||
page.should_not have_text "Name Equals Product1"
|
||||
end
|
||||
|
||||
it "displays the 'loading' splash" do
|
||||
page.should have_selector "div.loading", :text => "Loading Products..."
|
||||
end
|
||||
|
||||
it "loads appropriate products" do
|
||||
page.should have_field "product_name", :with => "Product1"
|
||||
page.should have_field "product_name", :with => "Product2"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise manager" do
|
||||
@@ -535,6 +793,9 @@ feature %q{
|
||||
p = product_supplied
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
first("div.option_tab_titles h6", :text => "Toggle Columns").click
|
||||
first("li.column-list-item", text: "Available On").click
|
||||
|
||||
page.should have_field "product_name", with: p.name
|
||||
page.should have_select "supplier", selected: s1.name
|
||||
|
||||
@@ -379,8 +379,8 @@ feature %q{
|
||||
|
||||
# Then my times should have been saved
|
||||
flash_message.should == 'Order cycles have been updated.'
|
||||
OrderCycle.all.map { |oc| oc.orders_open_at.sec }.should == [0, 2, 4]
|
||||
OrderCycle.all.map { |oc| oc.orders_close_at.sec }.should == [1, 3, 5]
|
||||
OrderCycle.order('id ASC').map { |oc| oc.orders_open_at.sec }.should == [0, 2, 4]
|
||||
OrderCycle.order('id ASC').map { |oc| oc.orders_close_at.sec }.should == [1, 3, 5]
|
||||
end
|
||||
|
||||
scenario "cloning an order cycle" do
|
||||
|
||||
@@ -30,7 +30,7 @@ feature 'shipping methods' do
|
||||
|
||||
sm = Spree::ShippingMethod.last
|
||||
sm.name.should == 'Carrier Pidgeon'
|
||||
sm.distributors.should == [d1, d2]
|
||||
sm.distributors.sort.should == [d1, d2].sort
|
||||
end
|
||||
|
||||
it "at checkout, user can only see shipping methods for their current distributor (checkout spec)"
|
||||
|
||||
@@ -21,123 +21,13 @@ feature %q{
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then I should see an error message
|
||||
page.should have_content "Please choose a distributor for this order."
|
||||
page.should have_content "That product is not available from the chosen distributor or order cycle"
|
||||
|
||||
# And the product should not have been added to my cart
|
||||
Spree::Order.last.line_items.should be_empty
|
||||
end
|
||||
|
||||
scenario "adding the first product to the cart", :future => true do
|
||||
# Given a product, some distributors and a defined shipping cost
|
||||
d1 = create(:distributor_enterprise, :name => "Green Grass")
|
||||
d2 = create(:distributor_enterprise, :name => "AusFarmers United")
|
||||
create(:product, :distributors => [d2])
|
||||
p = create(:product, :price => 12.34)
|
||||
create(:product_distribution, :product => p, :distributor => d1)
|
||||
|
||||
# ... with a flat rate distribution fee of $1.23
|
||||
ef = p.product_distributions.first.enterprise_fee
|
||||
ef.calculator = Spree::Calculator::FlatRate.new preferred_amount: 1.23
|
||||
ef.calculator.save!
|
||||
|
||||
# When I choose a distributor
|
||||
visit spree.root_path
|
||||
click_on "AusFarmers United"
|
||||
|
||||
# And I add an item to my cart from a different distributor
|
||||
visit spree.product_path p
|
||||
select(d1.name, :from => 'distributor_id')
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then the correct totals should be displayed
|
||||
page.should have_selector 'span.item-total', :text => '$12.34'
|
||||
page.should have_selector 'span.distribution-total', :text => '$1.23'
|
||||
page.should have_selector 'span.grand-total', :text => '$13.57'
|
||||
|
||||
# And the item should be in my cart
|
||||
order = Spree::Order.last
|
||||
line_item = order.line_items.first
|
||||
line_item.product.should == p
|
||||
|
||||
# And my order should have its distributor set to the chosen distributor
|
||||
order.distributor.should == d1
|
||||
end
|
||||
|
||||
context "adding a subsequent product to the cart" do
|
||||
it "when there are several valid distributors, allows a choice from these options" do
|
||||
# Given two products, both distributed by two distributors
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
p1 = create(:product, :distributors => [d1, d2])
|
||||
p2 = create(:product, :distributors => [d1, d2])
|
||||
|
||||
# When I add the first to my cart via d1
|
||||
visit spree.product_path p1
|
||||
select d1.name, :from => 'distributor_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I go to add the second, I should have a choice of distributor
|
||||
visit spree.product_path p2
|
||||
page.should have_selector '#distributor_id option', :text => d1.name
|
||||
page.should have_selector '#distributor_id option', :text => d2.name
|
||||
|
||||
# When I add the second, both should be in my cart, and my distributor should be the one chosen second
|
||||
select d2.name, :from => 'distributor_id'
|
||||
click_button 'Add To Cart'
|
||||
visit spree.cart_path
|
||||
page.should have_selector 'h4 a', :text => p1.name
|
||||
page.should have_selector 'h4 a', :text => p2.name
|
||||
page.should have_selector "#logo h1 a", :text => d2.name
|
||||
end
|
||||
|
||||
it "when the only valid distributor is the chosen one, does not allow the user to choose a distributor" do
|
||||
# Given two products, each at the same distributor
|
||||
d = create(:distributor_enterprise)
|
||||
p1 = create(:product, :distributors => [d])
|
||||
p2 = create(:product, :distributors => [d])
|
||||
|
||||
# When I add the first to my cart
|
||||
visit spree.product_path p1
|
||||
select d.name, :from => 'distributor_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I go to add the second, I should not have a choice of distributor
|
||||
visit spree.product_path p2
|
||||
page.should_not have_selector 'select#distributor_id'
|
||||
page.should have_selector '.distributor-fixed', :text => "Your distributor for this order is #{d.name}"
|
||||
|
||||
# When I add the second, both should be in my cart
|
||||
click_button 'Add To Cart'
|
||||
visit spree.cart_path
|
||||
page.should have_selector 'h4 a', :text => p1.name
|
||||
page.should have_selector 'h4 a', :text => p2.name
|
||||
end
|
||||
|
||||
it "when the only valid distributor differs from the chosen one, alerts the user and changes distributor on add to cart" do
|
||||
# Given two products, one available at only one distributor
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
p1 = create(:product, :distributors => [d1, d2])
|
||||
p2 = create(:product, :distributors => [d2])
|
||||
|
||||
# When I add the first to my cart
|
||||
visit spree.product_path p1
|
||||
select d1.name, from: 'distributor_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I go to add the second
|
||||
visit spree.product_path p2
|
||||
|
||||
# Then I should see a message offering to change distributor for my order
|
||||
page.should have_content "Your distributor for this order will be changed to #{d2.name} if you add this product to your cart."
|
||||
|
||||
# When I add the second to my cart
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then My distributor should have changed
|
||||
page.should have_selector "#logo h1 a", :text => d2.name
|
||||
end
|
||||
|
||||
it "does not allow the user to add a product from a distributor that cannot supply the cart's products" do
|
||||
# Given two products, each at a different distributor
|
||||
d1 = create(:distributor_enterprise)
|
||||
@@ -146,8 +36,8 @@ feature %q{
|
||||
p2 = create(:product, :distributors => [d2])
|
||||
|
||||
# When I add one of them to my cart
|
||||
select_distribution d1
|
||||
visit spree.product_path p1
|
||||
select d1.name, :from => 'distributor_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I attempt to add the other
|
||||
@@ -205,7 +95,7 @@ feature %q{
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then I should see an error message
|
||||
page.should have_content "Please choose a distributor and order cycle for this order."
|
||||
page.should have_content "Please choose an order cycle for this order."
|
||||
|
||||
# And the product should not have been added to my cart
|
||||
Spree::Order.last.line_items.should be_empty
|
||||
@@ -218,9 +108,8 @@ feature %q{
|
||||
oc = create(:simple_order_cycle, :distributors => [d], :variants => [p.master])
|
||||
|
||||
# When I add an item to my cart
|
||||
select_distribution d, oc
|
||||
visit spree.product_path p
|
||||
select d.name, :from => 'distributor_id'
|
||||
select oc.name, :from => 'order_cycle_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then the correct totals should be displayed
|
||||
@@ -239,144 +128,6 @@ feature %q{
|
||||
order.distributor.should == d
|
||||
order.order_cycle.should == oc
|
||||
end
|
||||
|
||||
scenario "adding a product to the cart with an invalid distribution combination" do
|
||||
# Given a product and some distributors
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
p = create(:product, :price => 12.34)
|
||||
oc1 = create(:simple_order_cycle, :distributors => [d1], :variants => [p.master])
|
||||
oc2 = create(:simple_order_cycle, :distributors => [d2], :variants => [p.master])
|
||||
|
||||
# When I attempt to add the product to my cart with an invalid distribution
|
||||
visit spree.product_path p
|
||||
select d1.name, :from => 'distributor_id'
|
||||
select oc2.name, :from => 'order_cycle_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then I should see an error message
|
||||
page.should have_content "That product is not available from the chosen distributor or order cycle."
|
||||
|
||||
# And the product should not be in my cart
|
||||
Spree::Order.last.line_items.should be_empty
|
||||
end
|
||||
|
||||
|
||||
context "adding a subsequent product to the cart" do
|
||||
it "when there are several valid order cycles, allows a choice from these options" do
|
||||
# Given two products, both distributed by two distributors
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
p1 = create(:product)
|
||||
p2 = create(:product)
|
||||
oc1 = create(:simple_order_cycle,
|
||||
:distributors => [d1, d2], :variants => [p1.master, p2.master])
|
||||
oc2 = create(:simple_order_cycle,
|
||||
:distributors => [d1, d2], :variants => [p1.master, p2.master])
|
||||
|
||||
# When I add the first to my cart via d1/oc1
|
||||
visit spree.product_path p1
|
||||
select d1.name, :from => 'distributor_id'
|
||||
select oc1.name, :from => 'order_cycle_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I go to add the second, I should have a choice of order cycle and distributor
|
||||
visit spree.product_path p2
|
||||
page.should have_selector '#distributor_id option', :text => d1.name
|
||||
page.should have_selector '#distributor_id option', :text => d2.name
|
||||
page.should have_selector '#order_cycle_id option', :text => oc1.name
|
||||
page.should have_selector '#order_cycle_id option', :text => oc2.name
|
||||
|
||||
# When I add the second, both should be in my cart, and my
|
||||
# distributor and order cycle should be the one chosen second
|
||||
select d2.name, :from => 'distributor_id'
|
||||
select oc2.name, :from => 'order_cycle_id'
|
||||
click_button 'Add To Cart'
|
||||
visit spree.cart_path
|
||||
page.should have_selector 'h4 a', :text => p1.name
|
||||
page.should have_selector 'h4 a', :text => p2.name
|
||||
page.should have_selector "#logo h1 a", :text => d2.name
|
||||
end
|
||||
|
||||
it "when the only valid order cycle is the chosen one, does not allow the user to choose an order cycle" do
|
||||
# Given two products, each at the same distributor
|
||||
d = create(:distributor_enterprise)
|
||||
p1 = create(:product)
|
||||
p2 = create(:product)
|
||||
oc = create(:simple_order_cycle, :distributors => [d],
|
||||
:variants => [p1.master, p2.master])
|
||||
|
||||
# When I add the first to my cart
|
||||
visit spree.product_path p1
|
||||
select d.name, :from => 'distributor_id'
|
||||
select oc.name, :from => 'order_cycle_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I go to add the second, I should not have a choice of distributor or order cycle
|
||||
visit spree.product_path p2
|
||||
page.should_not have_selector 'select#distributor_id'
|
||||
page.should have_selector '.distributor-fixed', :text => "Your distributor for this order is #{d.name}"
|
||||
page.should_not have_selector 'select#order_cycle_id'
|
||||
page.should have_selector '.order-cycle-fixed', :text => "Your order cycle for this order is #{oc.name}"
|
||||
|
||||
# When I add the second, both should be in my cart
|
||||
click_button 'Add To Cart'
|
||||
visit spree.cart_path
|
||||
page.should have_selector 'h4 a', :text => p1.name
|
||||
page.should have_selector 'h4 a', :text => p2.name
|
||||
end
|
||||
|
||||
it "when the only valid distributor differs from the chosen one, alerts the user and changes distributor on add to cart" do
|
||||
# Given two products, one available at only one distributor
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
p1 = create(:product)
|
||||
p2 = create(:product)
|
||||
oc1 = create(:simple_order_cycle, :distributors => [d1], :variants => [p1.master])
|
||||
oc2 = create(:simple_order_cycle, :distributors => [d2], :variants => [p1.master, p2.master])
|
||||
|
||||
# When I add the first to my cart
|
||||
visit spree.product_path p1
|
||||
select d1.name, from: 'distributor_id'
|
||||
select oc1.name, from: 'order_cycle_id'
|
||||
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I go to add the second
|
||||
visit spree.product_path p2
|
||||
|
||||
# Then I should see a message offering to change distributor for my order
|
||||
page.should have_content "Your distributor for this order will be changed to #{d2.name} if you add this product to your cart."
|
||||
|
||||
# When I add the second to my cart
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then my distributor should have changed
|
||||
page.should have_selector "#logo h1 a", :text => d2.name
|
||||
end
|
||||
|
||||
it "does not allow the user to add a product from an order cycle that cannot supply the cart's products" do
|
||||
# Given two products, each at a different order cycle
|
||||
d = create(:distributor_enterprise)
|
||||
p1 = create(:product)
|
||||
p2 = create(:product)
|
||||
oc1 = create(:simple_order_cycle, :distributors => [d], :variants => [p1.master])
|
||||
oc2 = create(:simple_order_cycle, :distributors => [d], :variants => [p2.master])
|
||||
|
||||
# When I add one of them to my cart
|
||||
visit spree.product_path p1
|
||||
select d.name, :from => 'distributor_id'
|
||||
select oc1.name, :from => 'order_cycle_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I attempt to add the other
|
||||
visit spree.product_path p2
|
||||
|
||||
# Then I should not be allowed to add the product
|
||||
page.should_not have_selector "button#add-to-cart-button"
|
||||
page.should have_content "Please complete your order from #{oc1.name} before shopping in a different order cycle."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "group buys" do
|
||||
@@ -386,8 +137,8 @@ feature %q{
|
||||
p = create(:product, :distributors => [d], :group_buy => true)
|
||||
|
||||
# When I add the item to my cart
|
||||
select_distribution d
|
||||
visit spree.product_path p
|
||||
select d.name, :from => 'distributor_id'
|
||||
fill_in "variants_#{p.master.id}", :with => 2
|
||||
fill_in "variant_attributes_#{p.master.id}_max_quantity", :with => 3
|
||||
click_button 'Add To Cart'
|
||||
@@ -407,8 +158,8 @@ feature %q{
|
||||
create(:variant, :product => p)
|
||||
|
||||
# When I add the item to my cart
|
||||
select_distribution d
|
||||
visit spree.product_path p
|
||||
select d.name, :from => 'distributor_id'
|
||||
fill_in "quantity", :with => 2
|
||||
fill_in "max_quantity", :with => 3
|
||||
click_button 'Add To Cart'
|
||||
@@ -438,8 +189,8 @@ feature %q{
|
||||
p = create(:product, :distributors => [d], :group_buy => true)
|
||||
|
||||
# When I add the item to my cart
|
||||
select_distribution d
|
||||
visit spree.product_path p
|
||||
select d.name, :from => 'distributor_id'
|
||||
fill_in "variants_#{p.master.id}", :with => 2
|
||||
fill_in "variant_attributes_#{p.master.id}_max_quantity", :with => 1
|
||||
click_button 'Add To Cart'
|
||||
|
||||
@@ -90,79 +90,5 @@ feature %q{
|
||||
page.all('#product-variants li input').count.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
context "viewing a product, it provides a choice of distributor when adding to cart" do
|
||||
it "works when no distributor is chosen" do
|
||||
# Given a distributor and a product under it
|
||||
distributor = create(:distributor_enterprise)
|
||||
product = create(:product, :distributors => [distributor])
|
||||
|
||||
# When we view the product
|
||||
visit spree.product_path(product)
|
||||
|
||||
# Then we should see a choice of distributor, with no default
|
||||
page.should have_selector "select#distributor_id option", :text => distributor.name
|
||||
page.should_not have_selector "select#distributor_id option[selected='selected']"
|
||||
end
|
||||
|
||||
it "displays the local distributor as the default choice when available for the current product" do
|
||||
# Given a distributor and a product under it
|
||||
distributor1 = create(:distributor_enterprise)
|
||||
distributor2 = create(:distributor_enterprise)
|
||||
product = create(:product, :distributors => [distributor1,distributor2])
|
||||
|
||||
# When we select the distributor and view the product
|
||||
visit spree.select_distributor_order_path(distributor1)
|
||||
visit spree.product_path(product)
|
||||
|
||||
# Then we should see our distributor as the default option when adding the item to our cart
|
||||
page.should have_selector "select#distributor_id option[value='#{distributor1.id}'][selected='selected']"
|
||||
end
|
||||
|
||||
it "works when viewing a product from a remote distributor" do
|
||||
# Given two distributors and our product under one
|
||||
distributor_product = create(:distributor_enterprise)
|
||||
distributor_no_product = create(:distributor_enterprise)
|
||||
product = create(:product, :distributors => [distributor_product])
|
||||
create(:product, :distributors => [distributor_no_product])
|
||||
|
||||
# When we select the distributor without the product and then view the product
|
||||
visit spree.select_distributor_order_path(distributor_no_product)
|
||||
visit spree.root_path
|
||||
visit spree.product_path(product)
|
||||
|
||||
# Then we should be told that our distributor will be set to the one with the product
|
||||
page.should_not have_selector "select#distributor_id"
|
||||
page.should have_content "our distributor for this order will be changed to #{distributor_product.name} if you add this product to your cart."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "selecting an order cycle" do
|
||||
|
||||
before(:each) do
|
||||
OrderCyclesHelper.class_eval do
|
||||
def order_cycles_enabled?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "displays the distributor and order cycle name on the home page when an order cycle is selected" do
|
||||
# Given a distributor with a product
|
||||
d = create(:distributor_enterprise, :name => 'Melb Uni Co-op')
|
||||
p = create(:product)
|
||||
oc = create(:simple_order_cycle, :name => 'Bulk Foods', :distributors => [d], :variants => [p.master])
|
||||
|
||||
# When I select the distributor and order cycle
|
||||
visit spree.product_path p
|
||||
select d.name, :from => 'distributor_id'
|
||||
select oc.name, :from => 'order_cycle_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then I should see the name of the distributor and order cycle that I've selected
|
||||
page.should have_content 'Melb Uni Co-op'
|
||||
page.should_not have_selector 'div.distributor-description'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -122,6 +122,7 @@ feature %q{
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then I should see a breakdown of my delivery fees:
|
||||
|
||||
checkout_fees_table.should ==
|
||||
[["Bananas - packing fee by supplier Supplier 1", "$3.00", ""],
|
||||
["Bananas - transport fee by supplier Supplier 1", "$4.00", ""],
|
||||
|
||||
@@ -279,25 +279,6 @@ feature %q{
|
||||
page.should have_selector "input[value='#{@oc1.id}'][checked='checked']"
|
||||
page.should have_selector "option[value='#{@d1.id}'][selected='selected']"
|
||||
end
|
||||
|
||||
scenario "selection form is not shown when there are products in the cart" do
|
||||
# Given a product
|
||||
d = create(:distributor_enterprise)
|
||||
p = create(:product, :distributors => [d])
|
||||
|
||||
# When I go to the products listing page, I should see the selection form
|
||||
visit spree.products_path
|
||||
page.should have_selector "#distribution-selection"
|
||||
|
||||
# When I add a product to the cart
|
||||
visit spree.product_path p
|
||||
select d.name, :from => 'distributor_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then I should no longer see the selection form
|
||||
visit spree.products_path
|
||||
page.should_not have_selector "#distribution-selection"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -51,24 +51,5 @@ feature %q{
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with Javascript", js: true do
|
||||
it "changes distributor details when the distributor is changed" do
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
d3 = create(:distributor_enterprise)
|
||||
p = create(:product, :distributors => [d1, d2, d3])
|
||||
|
||||
visit spree.product_path p
|
||||
|
||||
[d1, d2, d3].each do |d|
|
||||
select d.name, :from => 'distributor_id'
|
||||
|
||||
within '#product-distributor-details' do
|
||||
page.should have_selector 'h2', :text => d.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
136
spec/features/consumer/shopping_spec.rb
Normal file
136
spec/features/consumer/shopping_spec.rb
Normal file
@@ -0,0 +1,136 @@
|
||||
require 'spec_helper'
|
||||
|
||||
feature "As a consumer I want to shop with a distributor", js: true do
|
||||
include AuthenticationWorkflow
|
||||
include WebHelper
|
||||
|
||||
describe "Viewing a distributor" do
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
|
||||
before do #temporarily using the old way to select distributor
|
||||
create_enterprise_group_for distributor
|
||||
visit "/"
|
||||
click_link distributor.name
|
||||
end
|
||||
it "shows a distributor" do
|
||||
visit shop_path
|
||||
page.should have_text distributor.name
|
||||
end
|
||||
|
||||
describe "With products in order cycles" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:product, supplier: supplier) }
|
||||
let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) }
|
||||
|
||||
before do
|
||||
exchange = Exchange.find(order_cycle.exchanges.to_enterprises(distributor).outgoing.first.id)
|
||||
exchange.variants << product.master
|
||||
end
|
||||
|
||||
it "shows the suppliers/producers for a distributor" do
|
||||
visit shop_path
|
||||
click_link "Our Producers"
|
||||
page.should have_content supplier.name
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
describe "selecting an order cycle" do
|
||||
it "selects an order cycle if only one is open" do
|
||||
# create order cycle
|
||||
oc1 = create(:simple_order_cycle, distributors: [distributor])
|
||||
exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id)
|
||||
exchange.update_attribute :pickup_time, "turtles"
|
||||
|
||||
visit shop_path
|
||||
page.should have_selector "option[selected]", text: 'turtles'
|
||||
end
|
||||
|
||||
describe "with multiple order cycles" do
|
||||
let(:oc1) {create(:simple_order_cycle, distributors: [distributor])}
|
||||
let(:oc2) {create(:simple_order_cycle, distributors: [distributor])}
|
||||
before do
|
||||
exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id)
|
||||
exchange.update_attribute :pickup_time, "frogs"
|
||||
exchange = Exchange.find(oc2.exchanges.to_enterprises(distributor).outgoing.first.id)
|
||||
exchange.update_attribute :pickup_time, "turtles"
|
||||
end
|
||||
|
||||
it "shows a select with all order cycles" do
|
||||
visit shop_path
|
||||
page.should have_selector "option", text: 'frogs'
|
||||
page.should have_selector "option", text: 'turtles'
|
||||
end
|
||||
|
||||
describe "with products in our order cycle" do
|
||||
let(:product) { create(:simple_product) }
|
||||
before do
|
||||
exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id)
|
||||
exchange.variants << product.master
|
||||
visit shop_path
|
||||
end
|
||||
|
||||
it "allows us to select an order cycle" do
|
||||
select "frogs", :from => "order_cycle_id"
|
||||
Spree::Order.last.order_cycle.should == nil
|
||||
page.should have_selector "products"
|
||||
page.should have_content "Orders close #{oc1.orders_close_at.strftime('%A %m')}"
|
||||
Spree::Order.last.order_cycle.should == oc1
|
||||
end
|
||||
|
||||
it "doesn't show products before an order cycle is selected" do
|
||||
page.should_not have_content product.name
|
||||
end
|
||||
|
||||
it "shows products when an order cycle has been selected" do
|
||||
select "frogs", :from => "order_cycle_id"
|
||||
page.should have_content product.name
|
||||
end
|
||||
|
||||
it "updates the orders close note when order cycle is changed" do
|
||||
select "frogs", :from => "order_cycle_id"
|
||||
page.should have_content "Orders close #{oc1.orders_close_at.strftime('%A %m')}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "adding products to cart" do
|
||||
let(:oc) { create(:simple_order_cycle, distributors: [distributor]) }
|
||||
let(:product) { create(:simple_product) }
|
||||
let(:variant) { create(:variant, product: product) }
|
||||
before do
|
||||
exchange = Exchange.find(oc.exchanges.to_enterprises(distributor).outgoing.first.id)
|
||||
exchange.update_attribute :pickup_time, "frogs"
|
||||
exchange.variants << product.master
|
||||
exchange.variants << variant
|
||||
visit shop_path
|
||||
select "frogs", :from => "order_cycle_id"
|
||||
end
|
||||
it "should let us add products to our cart" do
|
||||
fill_in "variants[#{variant.id}]", with: "1"
|
||||
first("form.custom > input.button.right").click
|
||||
current_path.should == "/cart"
|
||||
page.should have_content product.name
|
||||
end
|
||||
end
|
||||
|
||||
context "when no order cycles are available" do
|
||||
it "tells us orders are closed" do
|
||||
visit shop_path
|
||||
page.should have_content "Orders are currently closed for this hub"
|
||||
end
|
||||
it "shows the last order cycle" do
|
||||
oc1 = create(:simple_order_cycle, distributors: [distributor], orders_close_at: 10.days.ago)
|
||||
visit shop_path
|
||||
page.should have_content "The last cycle closed 10 days ago"
|
||||
end
|
||||
it "shows the next order cycle" do
|
||||
oc1 = create(:simple_order_cycle, distributors: [distributor], orders_open_at: 10.days.from_now)
|
||||
visit shop_path
|
||||
page.should have_content "The next cycle opens in 10 days"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -33,4 +33,16 @@ describe OrderCyclesHelper do
|
||||
helper.pickup_time.should == "turtles"
|
||||
end
|
||||
|
||||
it "should give me the pickup time for any order cycle" do
|
||||
d = create(:distributor_enterprise, name: 'Green Grass')
|
||||
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"
|
||||
|
||||
helper.stub!(:current_order_cycle).and_return oc1
|
||||
helper.stub!(:current_distributor).and_return d
|
||||
helper.pickup_time(oc2).should == "turtles"
|
||||
end
|
||||
end
|
||||
|
||||
11
spec/helpers/shop_helper_spec.rb
Normal file
11
spec/helpers/shop_helper_spec.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
require 'spec_helper'
|
||||
describe ShopHelper do
|
||||
|
||||
it "should build order cycle select options" do
|
||||
d = create(:distributor_enterprise)
|
||||
o1 = create(:simple_order_cycle, distributors: [d])
|
||||
helper.stub(:current_distributor).and_return d
|
||||
|
||||
helper.order_cycles_name_and_pickup_times([o1]).should == [[helper.pickup_time(o1), o1.id]]
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
describe "filtering products", ->
|
||||
describe "filtering products for submission to database", ->
|
||||
it "accepts an object or an array and only returns an array", ->
|
||||
expect(filterSubmitProducts([])).toEqual []
|
||||
expect(filterSubmitProducts({})).toEqual []
|
||||
@@ -276,15 +276,41 @@ describe "AdminBulkProductsCtrl", ->
|
||||
it "gets a list of suppliers and then resets products with a list of data", ->
|
||||
httpBackend.expectGET("/api/users/authorise_api?token=api_key").respond success: "Use of API Authorised"
|
||||
httpBackend.expectGET("/api/enterprises/managed?template=bulk_index&q[is_primary_producer_eq]=true").respond "list of suppliers"
|
||||
httpBackend.expectGET("/api/products/managed?template=bulk_index;page=1;per_page=500").respond "list of products"
|
||||
spyOn scope, "resetProducts"
|
||||
spyOn(scope, "fetchProducts").andReturn "nothing"
|
||||
scope.initialise "api_key"
|
||||
httpBackend.flush()
|
||||
expect(scope.suppliers).toEqual "list of suppliers"
|
||||
expect(scope.resetProducts).toHaveBeenCalledWith "list of products"
|
||||
expect(scope.fetchProducts.calls.length).toEqual 1
|
||||
expect(scope.spree_api_key_ok).toEqual true
|
||||
|
||||
|
||||
describe "fetching products", ->
|
||||
it "makes a standard call to dataFetcher when no filters exist", ->
|
||||
httpBackend.expectGET("/api/products/managed?template=bulk_index;page=1;per_page=500;").respond "list of products"
|
||||
scope.fetchProducts()
|
||||
|
||||
it "calls resetProducts after data has been received", ->
|
||||
spyOn scope, "resetProducts"
|
||||
httpBackend.expectGET("/api/products/managed?template=bulk_index;page=1;per_page=500;").respond "list of products"
|
||||
scope.fetchProducts()
|
||||
httpBackend.flush()
|
||||
expect(scope.resetProducts).toHaveBeenCalledWith "list of products"
|
||||
|
||||
it "applies filters when they are present", ->
|
||||
filter = {property: scope.filterableColumns[1], predicate:scope.filterTypes[0], value:"Product1"}
|
||||
scope.currentFilters.push filter # Don't use addFilter as that is not what we are testing
|
||||
expect(scope.currentFilters).toEqual [filter]
|
||||
httpBackend.expectGET("/api/products/managed?template=bulk_index;page=1;per_page=500;q[name_eq]=Product1;").respond "list of products"
|
||||
scope.fetchProducts()
|
||||
|
||||
it "sets the loading property to true before fetching products and unsets it when loading is complete", ->
|
||||
httpBackend.expectGET("/api/products/managed?template=bulk_index;page=1;per_page=500;").respond "list of products"
|
||||
scope.fetchProducts()
|
||||
expect(scope.loading).toEqual true
|
||||
httpBackend.flush()
|
||||
expect(scope.loading).toEqual false
|
||||
|
||||
|
||||
describe "resetting products", ->
|
||||
beforeEach ->
|
||||
spyOn scope, "unpackProduct"
|
||||
@@ -681,6 +707,7 @@ describe "AdminBulkProductsCtrl", ->
|
||||
]
|
||||
scope.updateProducts "list of dirty products"
|
||||
httpBackend.flush()
|
||||
timeout.flush()
|
||||
expect(scope.displaySuccess).toHaveBeenCalled()
|
||||
|
||||
it "runs displayFailure() when post return data does not match $scope.products", ->
|
||||
@@ -972,6 +999,62 @@ describe "AdminBulkProductsCtrl", ->
|
||||
]
|
||||
|
||||
|
||||
describe "filtering products", ->
|
||||
describe "adding a filter to the filter list", ->
|
||||
it "adds objects sent to addFilter() to $scope.currentFilters", ->
|
||||
spyOn(scope, "fetchProducts").andReturn "nothing"
|
||||
filterObject1 = {property: scope.filterableColumns[0], predicate: scope.filterTypes[0], value: "Product1"}
|
||||
filterObject2 = {property: scope.filterableColumns[0], predicate: scope.filterTypes[0], value: "Product2"}
|
||||
scope.addFilter filterObject1
|
||||
scope.addFilter filterObject2
|
||||
expect(scope.currentFilters).toEqual [filterObject1, filterObject2]
|
||||
|
||||
it "ignores objects sent to addFilter() which do not contain a 'property' with a corresponding key in filterableColumns", ->
|
||||
spyOn(scope, "fetchProducts").andReturn "nothing"
|
||||
filterObject1 = {property: scope.filterableColumns[0], predicate: scope.filterTypes[0], value: "value1"}
|
||||
filterObject2 = {property: scope.filterableColumns[1], predicate: scope.filterTypes[0], value: "value2"}
|
||||
filterObject3 = {property: "some_random_property", predicate: scope.filterTypes[0], value: "value3"}
|
||||
scope.addFilter filterObject1
|
||||
scope.addFilter filterObject2
|
||||
scope.addFilter filterObject3
|
||||
expect(scope.currentFilters).toEqual [filterObject1, filterObject2]
|
||||
|
||||
it "ignores objects sent to addFilter() which do not contain a query with a corresponding key in filterTypes", ->
|
||||
spyOn(scope, "fetchProducts").andReturn "nothing"
|
||||
filterObject1 = {property: scope.filterableColumns[0], predicate: scope.filterTypes[0], value: "value1"}
|
||||
filterObject2 = {property: scope.filterableColumns[0], predicate: scope.filterTypes[1], value: "value2"}
|
||||
filterObject3 = {property: scope.filterableColumns[0], predicate: "something", value: "value3"}
|
||||
scope.addFilter filterObject1
|
||||
scope.addFilter filterObject2
|
||||
scope.addFilter filterObject3
|
||||
expect(scope.currentFilters).toEqual [filterObject1, filterObject2]
|
||||
|
||||
it "calls fetchProducts when adding a new filter", ->
|
||||
spyOn(scope, "fetchProducts").andReturn "nothing"
|
||||
scope.addFilter( { property: scope.filterableColumns[0], predicate: scope.filterTypes[0], value: "value1" } )
|
||||
expect(scope.fetchProducts.calls.length).toEqual(1)
|
||||
|
||||
describe "removing a filter from the filter list", ->
|
||||
filterObject1 = filterObject2 = null
|
||||
|
||||
beforeEach ->
|
||||
filterObject1 = {property: scope.filterableColumns[0], predicate: scope.filterTypes[0], value: "Product1"}
|
||||
filterObject2 = {property: scope.filterableColumns[0], predicate: scope.filterTypes[0], value: "Product2"}
|
||||
scope.currentFilters = [ filterObject1, filterObject2 ]
|
||||
|
||||
it "removes the specified filter from $scope.currentFilters and calls fetchProducts", ->
|
||||
spyOn(scope, "fetchProducts").andReturn "nothing"
|
||||
scope.removeFilter filterObject1
|
||||
expect(scope.currentFilters).toEqual [ filterObject2 ]
|
||||
expect(scope.fetchProducts.calls.length).toEqual 1
|
||||
|
||||
it "ignores filters which do not exist in currentFilters", ->
|
||||
spyOn(scope, "fetchProducts").andReturn "nothing"
|
||||
filterObject3 = {property: scope.filterableColumns[1], predicate: scope.filterTypes[1], value: "SomethingElse"}
|
||||
scope.removeFilter filterObject3
|
||||
expect(scope.currentFilters).toEqual [ filterObject1, filterObject2 ]
|
||||
expect(scope.fetchProducts.calls.length).toEqual 0
|
||||
|
||||
|
||||
describe "converting arrays of objects with ids to an object with ids as keys", ->
|
||||
it "returns an object", ->
|
||||
|
||||
40
spec/javascripts/unit/darkswarm/controllers_spec.js.coffee
Normal file
40
spec/javascripts/unit/darkswarm/controllers_spec.js.coffee
Normal file
@@ -0,0 +1,40 @@
|
||||
describe 'All controllers', ->
|
||||
describe 'ProductsCtrl', ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
event = null
|
||||
rootScope = null
|
||||
Product = null
|
||||
|
||||
beforeEach ->
|
||||
module('Shop')
|
||||
Product =
|
||||
all: ->
|
||||
update: ->
|
||||
data: "testy mctest"
|
||||
|
||||
inject ($controller, $rootScope) ->
|
||||
rootScope = $rootScope
|
||||
scope = $rootScope.$new()
|
||||
ctrl = $controller 'ProductsCtrl', {$scope: scope, Product : Product}
|
||||
|
||||
it 'Fetches products from Product', ->
|
||||
expect(scope.data).toEqual 'testy mctest'
|
||||
|
||||
|
||||
describe 'OrderCycleCtrl', ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
event = null
|
||||
rootScope = null
|
||||
product_ctrl = null
|
||||
OrderCycle = null
|
||||
|
||||
beforeEach ->
|
||||
module 'Shop'
|
||||
scope = {}
|
||||
inject ($controller, $rootScope) ->
|
||||
rootScope = $rootScope
|
||||
scope = $rootScope.$new()
|
||||
ctrl = $controller 'OrderCycleCtrl', {$scope: scope}
|
||||
|
||||
34
spec/javascripts/unit/darkswarm/order_cycle_spec.js.coffee
Normal file
34
spec/javascripts/unit/darkswarm/order_cycle_spec.js.coffee
Normal file
@@ -0,0 +1,34 @@
|
||||
describe 'OrderCycle service', ->
|
||||
$httpBackend = null
|
||||
OrderCycle = null
|
||||
mockProduct = {
|
||||
update: ->
|
||||
}
|
||||
|
||||
beforeEach ->
|
||||
angular.module('Shop').value('orderCycleData', {})
|
||||
module 'Shop', ($provide)->
|
||||
$provide.value "Product", mockProduct
|
||||
null # IMPORTANT
|
||||
# You must return null because module() is a bit dumb
|
||||
|
||||
inject (_OrderCycle_, _$httpBackend_)->
|
||||
$httpBackend = _$httpBackend_
|
||||
OrderCycle = _OrderCycle_
|
||||
|
||||
|
||||
it "posts the order_cycle ID and tells product to update", ->
|
||||
$httpBackend.expectPOST("/shop/order_cycle", {"order_cycle_id" : 10}).respond(200)
|
||||
spyOn(mockProduct, "update")
|
||||
OrderCycle.order_cycle.order_cycle_id = 10
|
||||
OrderCycle.push_order_cycle()
|
||||
$httpBackend.flush()
|
||||
expect(mockProduct.update).toHaveBeenCalled()
|
||||
|
||||
it "updates the orders_close_at attr after update", ->
|
||||
datestring = "2013-12-20T00:00:00+11:00"
|
||||
$httpBackend.expectPOST("/shop/order_cycle").respond({orders_close_at: datestring})
|
||||
OrderCycle.push_order_cycle()
|
||||
$httpBackend.flush()
|
||||
expect(OrderCycle.order_cycle.orders_close_at).toEqual(datestring)
|
||||
|
||||
14
spec/javascripts/unit/darkswarm/product_spec.js.coffee
Normal file
14
spec/javascripts/unit/darkswarm/product_spec.js.coffee
Normal file
@@ -0,0 +1,14 @@
|
||||
describe 'Product service', ->
|
||||
$httpBackend = null
|
||||
Product = null
|
||||
|
||||
beforeEach ->
|
||||
module 'Shop'
|
||||
inject ($injector, _$httpBackend_)->
|
||||
Product = $injector.get("Product")
|
||||
$httpBackend = _$httpBackend_
|
||||
|
||||
it "Fetches products from the backend on init", ->
|
||||
$httpBackend.expectGET("/shop/products").respond([{test : "cats"}])
|
||||
products = Product.all()
|
||||
$httpBackend.flush()
|
||||
@@ -754,4 +754,4 @@ describe 'OrderCycle services', ->
|
||||
expect(data.incoming_exchanges[0].enterprise_fees).toBeUndefined()
|
||||
expect(data.outgoing_exchanges[0].enterprise_fees).toBeUndefined()
|
||||
expect(data.incoming_exchanges[0].enterprise_fee_ids).toEqual [1, 2]
|
||||
expect(data.outgoing_exchanges[0].enterprise_fee_ids).toEqual [3, 4]
|
||||
expect(data.outgoing_exchanges[0].enterprise_fee_ids).toEqual [3, 4]
|
||||
|
||||
@@ -7,59 +7,20 @@ module Spree
|
||||
let(:params) { double(:params) }
|
||||
let(:distributor) { double(:distributor) }
|
||||
let(:order_cycle) { double(:order_cycle) }
|
||||
let(:orig_distributor) { double(:distributor) }
|
||||
let(:orig_order_cycle) { double(:order_cycle) }
|
||||
let(:op) { OrderPopulator.new(order, currency) }
|
||||
|
||||
describe "populate" do
|
||||
|
||||
it "checks that distribution can supply all products in the cart" do
|
||||
op.should_receive(:load_distributor_and_order_cycle).with(params).
|
||||
op.should_receive(:distributor_and_order_cycle).
|
||||
and_return([distributor, order_cycle])
|
||||
op.should_receive(:distribution_can_supply_products_in_cart).
|
||||
with(distributor, order_cycle).and_return(false)
|
||||
op.stub(:orig_distributor_and_order_cycle).and_return([orig_distributor,
|
||||
orig_order_cycle])
|
||||
op.should_receive(:populate_without_distribution_validation).never
|
||||
op.should_receive(:set_cart_distributor_and_order_cycle).never
|
||||
|
||||
op.populate(params).should be_false
|
||||
op.errors.to_a.should == ["That distributor or order cycle can't supply all the products in your cart. Please choose another."]
|
||||
end
|
||||
|
||||
it "resets cart distributor and order cycle if populate fails" do
|
||||
op.should_receive(:load_distributor_and_order_cycle).with(params).
|
||||
and_return([distributor, order_cycle])
|
||||
op.should_receive(:distribution_can_supply_products_in_cart).
|
||||
with(distributor, order_cycle).and_return(true)
|
||||
op.stub(:orig_distributor_and_order_cycle).and_return([orig_distributor,
|
||||
orig_order_cycle])
|
||||
|
||||
op.class_eval do
|
||||
def populate_without_distribution_validation(from_hash)
|
||||
errors.add(:base, "Something went wrong.")
|
||||
end
|
||||
end
|
||||
|
||||
op.should_receive(:set_cart_distributor_and_order_cycle).with(distributor, order_cycle)
|
||||
op.should_receive(:set_cart_distributor_and_order_cycle).with(orig_distributor, orig_order_cycle)
|
||||
|
||||
op.populate(params).should be_false
|
||||
op.errors.to_a.should == ["Something went wrong."]
|
||||
end
|
||||
|
||||
it "sets cart distributor and order cycle when populate succeeds" do
|
||||
op.should_receive(:load_distributor_and_order_cycle).with(params).
|
||||
and_return([distributor, order_cycle])
|
||||
op.should_receive(:distribution_can_supply_products_in_cart).
|
||||
with(distributor, order_cycle).and_return(true)
|
||||
op.stub(:orig_distributor_and_order_cycle).and_return([orig_distributor,
|
||||
orig_order_cycle])
|
||||
op.should_receive(:populate_without_distribution_validation).with(params)
|
||||
op.should_receive(:set_cart_distributor_and_order_cycle).with(distributor, order_cycle)
|
||||
|
||||
op.populate(params).should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "attempt_cart_add" do
|
||||
@@ -69,7 +30,7 @@ module Spree
|
||||
Spree::Variant.stub(:find).and_return(variant)
|
||||
|
||||
op.should_receive(:check_stock_levels).with(variant, quantity).and_return(true)
|
||||
op.should_receive(:check_distribution_provided_for).with(variant).and_return(true)
|
||||
op.should_receive(:check_order_cycle_provided_for).with(variant).and_return(true)
|
||||
op.should_receive(:check_variant_available_under_distribution).with(variant).
|
||||
and_return(true)
|
||||
order.should_receive(:add_variant).with(variant, quantity, currency)
|
||||
@@ -89,30 +50,22 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
describe "checking distribution is provided for a variant" do
|
||||
describe "checking order cycle is provided for a variant, OR is not needed" do
|
||||
let(:variant) { double(:variant) }
|
||||
|
||||
it "returns false and errors when distribution is not provided and order cycle is required" do
|
||||
op.should_receive(:distribution_provided_for).with(variant).and_return(false)
|
||||
op.should_receive(:order_cycle_required_for).with(variant).and_return(true)
|
||||
|
||||
op.send(:check_distribution_provided_for, variant).should be_false
|
||||
op.errors.to_a.should == ["Please choose a distributor and order cycle for this order."]
|
||||
it "returns false and errors when order cycle is not provided and is required" do
|
||||
op.stub(:order_cycle_required_for).and_return true
|
||||
op.send(:check_order_cycle_provided_for, variant).should be_false
|
||||
op.errors.to_a.should == ["Please choose an order cycle for this order."]
|
||||
end
|
||||
|
||||
it "returns false and errors when distribution is not provided and order cycle is not required" do
|
||||
op.should_receive(:distribution_provided_for).with(variant).and_return(false)
|
||||
op.should_receive(:order_cycle_required_for).with(variant).and_return(false)
|
||||
|
||||
op.send(:check_distribution_provided_for, variant).should be_false
|
||||
op.errors.to_a.should == ["Please choose a distributor for this order."]
|
||||
it "returns true when order cycle is provided" do
|
||||
op.stub(:order_cycle_required_for).and_return true
|
||||
op.instance_variable_set :@order_cycle, double(:order_cycle)
|
||||
op.send(:check_order_cycle_provided_for, variant).should be_true
|
||||
end
|
||||
|
||||
it "returns true and does not error otherwise" do
|
||||
op.should_receive(:distribution_provided_for).with(variant).and_return(true)
|
||||
|
||||
op.send(:check_distribution_provided_for, variant).should be_true
|
||||
op.errors.should be_empty
|
||||
it "returns true when order cycle is not required" do
|
||||
op.stub(:order_cycle_required_for).and_return false
|
||||
op.send(:check_order_cycle_provided_for, variant).should be_true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -142,60 +95,6 @@ module Spree
|
||||
|
||||
|
||||
describe "support" do
|
||||
describe "loading distributor and order cycle from hash" do
|
||||
it "loads distributor and order cycle when present" do
|
||||
params = {distributor_id: 1, order_cycle_id: 2}
|
||||
distributor = double(:distributor)
|
||||
order_cycle = double(:order_cycle)
|
||||
|
||||
enterprise_scope = double(:enterprise_scope)
|
||||
enterprise_scope.should_receive(:find).with(1).and_return(distributor)
|
||||
Enterprise.should_receive(:is_distributor).and_return(enterprise_scope)
|
||||
OrderCycle.should_receive(:find).with(2).and_return(order_cycle)
|
||||
|
||||
op.send(:load_distributor_and_order_cycle, params).should ==
|
||||
[distributor, order_cycle]
|
||||
end
|
||||
|
||||
it "returns nil when not present" do
|
||||
op.send(:load_distributor_and_order_cycle, {}).should == [nil, nil]
|
||||
end
|
||||
end
|
||||
|
||||
it "sets cart distributor and order cycle" do
|
||||
Spree::Order.should_receive(:find).with(order.id).and_return(order)
|
||||
order.should_receive(:set_distribution!).with(distributor, order_cycle)
|
||||
|
||||
op.send(:set_cart_distributor_and_order_cycle, distributor, order_cycle)
|
||||
end
|
||||
|
||||
describe "checking if distribution is provided for a variant" do
|
||||
let(:variant) { double(:variant) }
|
||||
|
||||
it "returns false when distributor is nil" do
|
||||
op.instance_eval { @distributor = nil }
|
||||
op.send(:distribution_provided_for, variant).should be_false
|
||||
end
|
||||
|
||||
it "returns false when order cycle is nil when it's required" do
|
||||
op.instance_eval { @distributor = 1; @order_cycle = nil }
|
||||
op.should_receive(:order_cycle_required_for).with(variant).and_return(true)
|
||||
op.send(:distribution_provided_for, variant).should be_false
|
||||
end
|
||||
|
||||
it "returns true when distributor is present and order cycle is not required" do
|
||||
op.instance_eval { @distributor = 1; @order_cycle = nil }
|
||||
op.should_receive(:order_cycle_required_for).with(variant).and_return(false)
|
||||
op.send(:distribution_provided_for, variant).should be_true
|
||||
end
|
||||
|
||||
it "returns true when distributor is present and order cycle is required and present" do
|
||||
op.instance_eval { @distributor = 1; @order_cycle = 1 }
|
||||
op.should_receive(:order_cycle_required_for).with(variant).and_return(true)
|
||||
op.send(:distribution_provided_for, variant).should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "checking if order cycle is required for a variant" do
|
||||
it "requires an order cycle when the product has no product distributions" do
|
||||
product = double(:product, product_distributions: [])
|
||||
@@ -210,11 +109,11 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
it "provides the original distributor and order cycle for the order" do
|
||||
order.should_receive(:distributor).and_return(orig_distributor)
|
||||
order.should_receive(:order_cycle).and_return(orig_order_cycle)
|
||||
op.send(:orig_distributor_and_order_cycle).should == [orig_distributor,
|
||||
orig_order_cycle]
|
||||
it "provides the distributor and order cycle for the order" do
|
||||
order.should_receive(:distributor).and_return(distributor)
|
||||
order.should_receive(:order_cycle).and_return(order_cycle)
|
||||
op.send(:distributor_and_order_cycle).should == [distributor,
|
||||
order_cycle]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ module Spree
|
||||
sm.distributors << d1
|
||||
sm.distributors << d2
|
||||
|
||||
sm.reload.distributors.should == [d1, d2]
|
||||
sm.reload.distributors.sort.should == [d1, d2].sort
|
||||
end
|
||||
|
||||
it "finds shipping methods for a particular distributor" do
|
||||
|
||||
@@ -32,13 +32,15 @@ ActiveRecord::Fixtures.create_fixtures(fixtures_dir, ['spree/states', 'spree/cou
|
||||
require 'capybara/poltergeist'
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
|
||||
# For debugging:
|
||||
# - Extend poltergeist's timeout to allow ample time to use pry in browser thread
|
||||
# - Enable the remote inspector: Use page.driver.debug to open a remote debugger in chrome
|
||||
# Capybara.register_driver :poltergeist do |app|
|
||||
# Capybara::Poltergeist::Driver.new(app, timeout: 5.minutes)
|
||||
# Capybara::Poltergeist::Driver.new(app, inspector: true)
|
||||
# end
|
||||
Capybara.register_driver :poltergeist do |app|
|
||||
options = {phantomjs_options: ['--load-images=no']}
|
||||
# Extend poltergeist's timeout to allow ample time to use pry in browser thread
|
||||
#options.merge! {timeout: 5.minutes}
|
||||
# Enable the remote inspector: Use page.driver.debug to open a remote debugger in chrome
|
||||
#options.merge! {inspector: true}
|
||||
|
||||
Capybara::Poltergeist::Driver.new(app, options)
|
||||
end
|
||||
|
||||
|
||||
require "paperclip/matchers"
|
||||
@@ -92,6 +94,7 @@ RSpec.configure do |config|
|
||||
config.include Devise::TestHelpers, :type => :controller
|
||||
config.include OpenFoodNetwork::FeatureToggleHelper
|
||||
config.include OpenFoodNetwork::EnterpriseGroupsHelper
|
||||
config.include OpenFoodNetwork::DistributionHelper
|
||||
config.include ActionView::Helpers::DateHelper
|
||||
|
||||
# Factory girl
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
module AuthenticationWorkflow
|
||||
def login_to_admin_section
|
||||
admin_role = Spree::Role.find_or_create_by_name!('admin')
|
||||
admin_user = Spree::User.create!({
|
||||
:email => 'admin@ofn.org',
|
||||
admin_user = create(:user,
|
||||
:password => 'passw0rd',
|
||||
:password_confirmation => 'passw0rd',
|
||||
:remember_me => false,
|
||||
:persistence_token => 'pass',
|
||||
:login => 'admin@ofn.org'})
|
||||
:login => 'admin@ofn.org')
|
||||
|
||||
admin_user.spree_roles << admin_role
|
||||
|
||||
|
||||
14
spec/support/request/distribution_helper.rb
Normal file
14
spec/support/request/distribution_helper.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
module OpenFoodNetwork
|
||||
module DistributionHelper
|
||||
|
||||
def select_distribution(distributor, order_cycle=nil)
|
||||
create_enterprise_group_for distributor
|
||||
visit root_path
|
||||
click_link distributor.name
|
||||
|
||||
if order_cycle && page.has_select?('order_order_cycle_id')
|
||||
select_by_value order_cycle.id, from: 'order_order_cycle_id'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user