mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge branch 'new_shop'
Conflicts: app/controllers/checkout_controller.rb
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
Darkswarm.controller "GroupsCtrl", ($scope, Groups) ->
|
||||
$scope.Groups = Groups
|
||||
$scope.order = 'position'
|
||||
@@ -0,0 +1,9 @@
|
||||
Darkswarm.controller "ProductNodeCtrl", ($scope) ->
|
||||
$scope.price = ->
|
||||
if $scope.product.variants.length > 0
|
||||
prices = (v.price for v in $scope.product.variants)
|
||||
Math.min.apply(null, prices)
|
||||
else
|
||||
$scope.product.price
|
||||
|
||||
$scope.hasVariants = $scope.product.variants.length > 0
|
||||
@@ -1,8 +1,8 @@
|
||||
Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Product, OrderCycle) ->
|
||||
$scope.data = Product.data
|
||||
$scope.limit = 3
|
||||
$scope.ordering = {order: "name"}
|
||||
$scope.order_cycle = OrderCycle.order_cycle
|
||||
Product.update()
|
||||
|
||||
$scope.incrementLimit = ->
|
||||
if $scope.limit < $scope.data.products.length
|
||||
@@ -14,8 +14,3 @@ Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Product, OrderCycle) -
|
||||
e.preventDefault()
|
||||
|
||||
$scope.productPrice = (product) ->
|
||||
if product.variants.length > 0
|
||||
prices = (v.price for v in product.variants)
|
||||
Math.min.apply(null, prices)
|
||||
else
|
||||
product.price
|
||||
|
||||
@@ -2,13 +2,14 @@ Darkswarm.directive "ofnModal", ($modal)->
|
||||
restrict: 'E'
|
||||
replace: true
|
||||
transclude: true
|
||||
scope: {}
|
||||
template: "<a>{{title}}</a>"
|
||||
|
||||
link: (scope, elem, attrs, ctrl, transclude)->
|
||||
scope.title = attrs.title
|
||||
contents = null
|
||||
transclude scope, (clone)->
|
||||
contents = clone
|
||||
|
||||
scope.cancel = ->
|
||||
scope.modalInstance.dismiss("cancel")
|
||||
|
||||
elem.on "click", ->
|
||||
scope.modalInstance = $modal.open(controller: ctrl, template: transclude())
|
||||
elem.on "click", =>
|
||||
scope.modalInstance = $modal.open(controller: ctrl, template: contents)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Darkswarm.factory 'Groups', (groups) ->
|
||||
new class Groups
|
||||
constructor: ->
|
||||
@groups = groups
|
||||
@@ -1,12 +1,12 @@
|
||||
Darkswarm.factory 'Product', ($resource) ->
|
||||
new class Product
|
||||
data: {
|
||||
constructor: ->
|
||||
@update()
|
||||
data:
|
||||
products: null
|
||||
loading: true
|
||||
}
|
||||
|
||||
update: ->
|
||||
@data.products = $resource("/shop/products").query =>
|
||||
@data.loading = false
|
||||
@data
|
||||
all: ->
|
||||
@data.products || @update()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
border: 1px solid #999
|
||||
font-size: 18px
|
||||
@extend .avenir
|
||||
padding: 22px 18px
|
||||
padding: 0.75em 1em
|
||||
height: auto
|
||||
margin-bottom: 1em
|
||||
|
||||
|
||||
4
app/assets/stylesheets/darkswarm/product_table.css.sass
Normal file
4
app/assets/stylesheets/darkswarm/product_table.css.sass
Normal file
@@ -0,0 +1,4 @@
|
||||
.product_table
|
||||
.row
|
||||
border: 1px solid black
|
||||
padding: 8px inherit
|
||||
@@ -1,8 +1,7 @@
|
||||
@import mixins
|
||||
@import variables
|
||||
@import branding
|
||||
|
||||
product
|
||||
display: block
|
||||
|
||||
.darkswarm
|
||||
#search
|
||||
@@ -77,68 +76,58 @@ product
|
||||
|
||||
products
|
||||
display: block
|
||||
padding-top: 2.3em
|
||||
padding-top: 2.3em
|
||||
@media all and (max-width: 768px)
|
||||
padding-top: 1em
|
||||
input.button.right
|
||||
float: left
|
||||
table
|
||||
table-layout: fixed
|
||||
width: 100%
|
||||
border-collapse: collapse
|
||||
border: none
|
||||
th
|
||||
line-height: 50px
|
||||
&.name
|
||||
width: 330px
|
||||
//&.notes
|
||||
//width: 140px
|
||||
&.variant
|
||||
width: 180px
|
||||
&.quantity, &.bulk, &.price
|
||||
width: 90px
|
||||
.notes
|
||||
max-width: 300px
|
||||
td, th
|
||||
|
||||
|
||||
product:hover, product:focus, product:active
|
||||
border-color: $clr-brick
|
||||
@include box-shadow(0 0 3px 0 $clr-brick-bright)
|
||||
|
||||
.row.variants
|
||||
border-top: 1px solid $clr-brick-light
|
||||
background: $clr-brick-ultra-light
|
||||
|
||||
product
|
||||
@include csstrans
|
||||
border: 1px solid #989898
|
||||
display: block
|
||||
margin-bottom: 1em !important
|
||||
|
||||
input
|
||||
margin: 0
|
||||
width: 8em
|
||||
|
||||
.columns
|
||||
padding-top: 1em
|
||||
padding-bottom: 1em
|
||||
|
||||
.row.summary, .row.variants
|
||||
@include csstrans
|
||||
margin-left: 0
|
||||
margin-right: 0
|
||||
background: #f7f7f7
|
||||
border-top: 1px solid #dfdfdf
|
||||
|
||||
.row.summary
|
||||
@include csstrans
|
||||
background: #fff
|
||||
> span
|
||||
min-width: 50px
|
||||
display: block
|
||||
tbody
|
||||
border: 1px solid #cccccc
|
||||
border-left: 0px
|
||||
border-right: 0px
|
||||
td
|
||||
padding: 20px 0px
|
||||
&.name
|
||||
img
|
||||
float: left
|
||||
margin-right: 30px
|
||||
@media all and (max-width: 768px)
|
||||
margin-right: 1em
|
||||
div
|
||||
min-width: 150px
|
||||
tr.product-description
|
||||
display: none
|
||||
|
||||
.summary-header
|
||||
&, & *
|
||||
@include avenir
|
||||
color: $clr-brick
|
||||
|
||||
.summary-price
|
||||
&, & *
|
||||
@include avenir
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Responsive
|
||||
@media all and (max-width: 768px)
|
||||
td.notes, th.notes
|
||||
display: none
|
||||
img
|
||||
width: 20px
|
||||
height: auto
|
||||
tr.product-description
|
||||
display: table-row
|
||||
td:empty
|
||||
display: none
|
||||
|
||||
|
||||
input[type=number]
|
||||
width: 60px
|
||||
margin: 0px
|
||||
display: block
|
||||
float: right
|
||||
padding-top: 14px
|
||||
|
||||
|
||||
3
app/assets/stylesheets/groups.css.scss
Normal file
3
app/assets/stylesheets/groups.css.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
// Place all the styles related to the groups controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@@ -15,12 +15,10 @@ module Admin
|
||||
redirect_to main_app.admin_enterprise_groups_path
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def collection
|
||||
EnterpriseGroup.by_position
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,7 +26,11 @@ class CheckoutController < Spree::CheckoutController
|
||||
if @order.next
|
||||
state_callback(:after)
|
||||
else
|
||||
flash[:error] = @order.errors.full_messages.to_sentence
|
||||
unless @order.errors.empty?
|
||||
flash[:error] = @order.errors.full_messages.to_sentence
|
||||
else
|
||||
flash[:error] = t(:payment_processing_failed)
|
||||
end
|
||||
update_failed
|
||||
return
|
||||
end
|
||||
|
||||
7
app/controllers/groups_controller.rb
Normal file
7
app/controllers/groups_controller.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
class GroupsController < BaseController
|
||||
layout 'darkswarm'
|
||||
|
||||
def index
|
||||
@groups = EnterpriseGroup.on_front_page.by_position
|
||||
end
|
||||
end
|
||||
@@ -11,4 +11,4 @@ Spree::Admin::BaseController.class_eval do
|
||||
authorize! :admin, record
|
||||
authorize! action, record
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,4 +4,17 @@ Spree::Admin::OverviewController.class_eval do
|
||||
@product_count = Spree::Product.active.managed_by(spree_current_user).count
|
||||
@order_cycle_count = OrderCycle.active.managed_by(spree_current_user).count
|
||||
end
|
||||
end
|
||||
|
||||
# This is in Spree::Core::ControllerHelpers::Auth
|
||||
# But you can't easily reopen modules in Ruby
|
||||
def unauthorized
|
||||
if try_spree_current_user
|
||||
flash[:error] = t(:authorization_failure)
|
||||
redirect_to '/unauthorized'
|
||||
else
|
||||
store_location
|
||||
url = respond_to?(:spree_login_path) ? spree_login_path : root_path
|
||||
redirect_to url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
2
app/helpers/groups_helper.rb
Normal file
2
app/helpers/groups_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
module GroupsHelper
|
||||
end
|
||||
@@ -4,6 +4,17 @@ class EnterpriseGroup < ActiveRecord::Base
|
||||
has_and_belongs_to_many :enterprises
|
||||
|
||||
validates :name, presence: true
|
||||
validates :description, presence: true
|
||||
|
||||
attr_accessible :name, :description, :long_description, :on_front_page, :enterprise_ids
|
||||
|
||||
attr_accessible :promo_image
|
||||
has_attached_file :promo_image, styles: {medium: "800>400"}
|
||||
validates_attachment_content_type :promo_image, :content_type => /\Aimage\/.*\Z/
|
||||
|
||||
attr_accessible :logo
|
||||
has_attached_file :logo, styles: {medium: "100x100"}
|
||||
validates_attachment_content_type :logo, :content_type => /\Aimage\/.*\Z/
|
||||
|
||||
scope :by_position, order('position ASC')
|
||||
scope :on_front_page, where(on_front_page: true)
|
||||
|
||||
@@ -118,6 +118,10 @@ Spree::Product.class_eval do
|
||||
order_cycle.variants_distributed_by(distributor).where(product_id: self)
|
||||
end
|
||||
|
||||
def primary_taxon
|
||||
self.taxons.order.first
|
||||
end
|
||||
|
||||
# Build a product distribution for each distributor
|
||||
def build_product_distributions_for_user user
|
||||
Enterprise.is_distributor.managed_by(user).each do |distributor|
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
%br/
|
||||
= f.text_field :name
|
||||
|
||||
= f.field_container :description do
|
||||
= f.label :description
|
||||
%br/
|
||||
= f.text_field :description
|
||||
|
||||
= f.field_container :long_description do
|
||||
= f.label :long_description
|
||||
%br/
|
||||
= f.text_area :long_description
|
||||
|
||||
= f.field_container :on_front_page do
|
||||
= f.label :on_front_page, 'On front page?'
|
||||
%br/
|
||||
@@ -12,3 +22,22 @@
|
||||
= f.label :enterprise_ids, 'Enterprises'
|
||||
%br/
|
||||
= f.collection_select :enterprise_ids, Enterprise.all, :id, :name, {}, {class: "select2 fullwidth", multiple: true}
|
||||
|
||||
|
||||
.row
|
||||
.alpha.three.columns
|
||||
= f.label :logo, class: 'with-tip', 'data-powertip' => 'This is the logo'
|
||||
.with-tip{'data-powertip' => 'This is the logo'}
|
||||
%a What's this?
|
||||
.omega.eight.columns
|
||||
= image_tag @object.logo.url if @object.logo.present?
|
||||
= f.file_field :logo
|
||||
|
||||
.row
|
||||
.alpha.three.columns
|
||||
= f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed at the top of the Group profile'
|
||||
.with-tip{'data-powertip' => 'This image is displayed at the top of the Group profile'}
|
||||
%a What's this?
|
||||
.omega.eight.columns
|
||||
= image_tag @object.promo_image.url if @object.promo_image.present?
|
||||
= f.file_field :promo_image
|
||||
|
||||
25
app/views/groups/index.html.haml
Normal file
25
app/views/groups/index.html.haml
Normal file
@@ -0,0 +1,25 @@
|
||||
#groups{"ng-controller" => "GroupsCtrl"}
|
||||
:javascript
|
||||
angular.module('Darkswarm').value('groups', #{render partial: "json/groups", object: @groups})
|
||||
.row
|
||||
.small-12.columns.text-center
|
||||
%h1 Groups / Regions
|
||||
%h3 Check out our food groups below
|
||||
|
||||
%input{type: :text,
|
||||
"ng-model" => "query",
|
||||
placeholder: "Search group name",
|
||||
"ng-debounce" => "150",
|
||||
"ofn-disable-enter" => true}
|
||||
|
||||
.row.group_table{bindonce: true}
|
||||
.small.12.columns
|
||||
.group{"ng-repeat" => "group in Groups.groups | filter:query | orderBy:order"}
|
||||
%h2 {{ group.name }}
|
||||
%p {{ group.description }}
|
||||
%p {{ group.long_description }}
|
||||
%img{"bo-src" => "group.logo"}
|
||||
|
||||
%ul
|
||||
%li{"ng-repeat" => "enterprise in group.enterprises"}
|
||||
%a{"bo-href" => "enterprise.path"} {{ enterprise.name }}
|
||||
@@ -2,7 +2,8 @@
|
||||
.columns.small-4
|
||||
%strong Shop for
|
||||
%p.trans-sentence
|
||||
{{ hub.taxons | printArrayOfObjects }}
|
||||
%img{"ng-repeat" => "taxon in hub.taxons", "bo-src" => "taxon.icon",
|
||||
name: "{{taxon.name}}", alt: "{{taxon.name}}"}
|
||||
.columns.small-4
|
||||
%strong Delivery options
|
||||
%ol
|
||||
@@ -10,8 +11,9 @@
|
||||
%li.delivery{"bo-if" => "hub.delivery"} Delivery
|
||||
.columns.small-4
|
||||
%strong Our producers
|
||||
%p
|
||||
Go to our shop to see our current producers
|
||||
%ul
|
||||
%li{"ng-repeat" => "producer in hub.producers"}
|
||||
= render partial: "modals/producer"
|
||||
|
||||
.row.active_table_row.link{"ng-show" => "open()", "ng-if" => "hub.active"}
|
||||
.columns.small-11
|
||||
|
||||
@@ -4,3 +4,11 @@ attributes :name, :id, :description
|
||||
child :address do
|
||||
extends "json/partials/address"
|
||||
end
|
||||
|
||||
node :path do |enterprise|
|
||||
shop_enterprise_path(enterprise)
|
||||
end
|
||||
|
||||
node :hash do |enterprise|
|
||||
enterprise.to_param
|
||||
end
|
||||
|
||||
10
app/views/json/_groups.rabl
Normal file
10
app/views/json/_groups.rabl
Normal file
@@ -0,0 +1,10 @@
|
||||
collection @groups
|
||||
attributes :id, :name, :position, :description, :long_description
|
||||
|
||||
child enterprises: :enterprises do
|
||||
extends 'json/enterprises'
|
||||
end
|
||||
|
||||
node :logo do |group|
|
||||
group.logo(:original)
|
||||
end
|
||||
@@ -2,11 +2,15 @@ collection Enterprise.visible.is_distributor
|
||||
extends 'json/enterprises'
|
||||
|
||||
child distributed_taxons: :taxons do
|
||||
attributes :name, :id
|
||||
extends "json/taxon"
|
||||
end
|
||||
|
||||
child suppliers: :producers do
|
||||
attributes :name, :id
|
||||
attributes :name, :id, :description, :long_description
|
||||
|
||||
node :promo_image do |producer|
|
||||
producer.promo_image.url
|
||||
end
|
||||
end
|
||||
|
||||
node :pickup do |hub|
|
||||
@@ -17,14 +21,6 @@ node :delivery do |hub|
|
||||
not hub.shipping_methods.where(:require_ship_address => true).empty?
|
||||
end
|
||||
|
||||
node :path do |hub|
|
||||
shop_enterprise_path(hub)
|
||||
end
|
||||
|
||||
node :hash do |hub|
|
||||
hub.to_param
|
||||
end
|
||||
|
||||
node :active do |hub|
|
||||
@active_distributors.include?(hub)
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ collection @producers
|
||||
extends 'json/enterprises'
|
||||
|
||||
child supplied_taxons: :taxons do
|
||||
attributes :name, :id
|
||||
extends 'json/taxon'
|
||||
end
|
||||
|
||||
child distributors: :distributors do
|
||||
|
||||
5
app/views/json/_taxon.rabl
Normal file
5
app/views/json/_taxon.rabl
Normal file
@@ -0,0 +1,5 @@
|
||||
attributes :name, :id, :permalink
|
||||
|
||||
node :icon do |taxon|
|
||||
taxon.icon.url
|
||||
end
|
||||
@@ -2,4 +2,4 @@
|
||||
%h5 Our food hubs are the point of contact between you and the people who make your food!
|
||||
%p You can search for a convenient hub by location or name. Some hubs have multiple points where you can pick-up your purchases, and some will also provide delivery options. Each food hub is a sales point with independent business operations and logisitics - so variations between hubs are to be expected.
|
||||
%p You can only shop one food hub at a time.
|
||||
%a.close-reveal-modal{"ng-click" => "cancel()"} ×
|
||||
%a.close-reveal-modal{"ng-click" => "$close()"} ×
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
%h5 Learn more
|
||||
%p If you want to learn more about the Open Food Network, how it works, and get involved, check out:
|
||||
%a.button.neutral-btn.dark{:href => "http://www.openfoodnetwork.org" , :target => "_blank" } Open Food Network
|
||||
%a.close-reveal-modal{"ng-click" => "cancel()"} ×
|
||||
%a.close-reveal-modal{"ng-click" => "$close()"} ×
|
||||
|
||||
13
app/views/modals/_producer.html.haml
Normal file
13
app/views/modals/_producer.html.haml
Normal file
@@ -0,0 +1,13 @@
|
||||
%ofn-modal{title: "{{ producer.name }}"}
|
||||
#producer_modal{bindonce: true}
|
||||
.row
|
||||
.small-12.columns
|
||||
%img{"bo-src" => "producer.promo_image"}
|
||||
%h3 {{ producer.name }}
|
||||
|
||||
.row
|
||||
.small-6.columns
|
||||
%p
|
||||
{{ producer.description }}
|
||||
.small-6.columns
|
||||
Stay in touch with {{ producer.name }}
|
||||
@@ -21,7 +21,7 @@
|
||||
%span.nav-primary Producers
|
||||
%li.divider
|
||||
%li
|
||||
%a{href: ""}
|
||||
%a{href: main_app.groups_path}
|
||||
%span.nav-primary Groups
|
||||
%li.divider
|
||||
- if admin_user? or enterprise_user?
|
||||
|
||||
@@ -36,17 +36,14 @@
|
||||
%li
|
||||
%a{href: root_path + "#/#hubs"}
|
||||
%span.nav-primary Hubs
|
||||
|
||||
%li
|
||||
%a{href: ""}
|
||||
%span.nav-primary Map
|
||||
|
||||
%li
|
||||
%a{href: main_app.producers_path}
|
||||
%span.nav-primary Producers
|
||||
|
||||
%li
|
||||
%a{href: ""}
|
||||
%a{href: main_app.groups_path}
|
||||
%span.nav-primary Groups
|
||||
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
%products{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null",
|
||||
"infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1"}
|
||||
|
||||
= form_for :order, :url => populate_orders_path, html: {:class => "custom"} do
|
||||
|
||||
%input#search.text{"ng-model" => "query",
|
||||
placeholder: "Search",
|
||||
"ng-debounce" => "150",
|
||||
"ng-keypress" => "searchKeypress($event)"}
|
||||
%input.button.right{type: :submit, value: "Add to Cart"}
|
||||
|
||||
%table
|
||||
%thead
|
||||
%th.name Item
|
||||
%th.notes Notes
|
||||
%th.variant Unit
|
||||
%th.quantity QTY
|
||||
%th.bulk Bulk
|
||||
%th.price.text-right Price
|
||||
%tbody{"ng-show" => "data.loading"}
|
||||
%tr
|
||||
%td{colspan: 6}
|
||||
%h3.text-center Loading Products
|
||||
%tbody{"ng-repeat" => "product in data.products | filter:query | limitTo: limit track by product.id"}
|
||||
%tr{"class" => "product product-{{ product.id }}"}
|
||||
|
||||
%td.name{bindonce: "product"}
|
||||
%img{"bo-src" => "product.master.images[0].small_url"}
|
||||
%div
|
||||
%h5
|
||||
{{ product.name }}
|
||||
%a{"data-reveal-id" => "producer_details_{{product.supplier.id}}", "data-reveal" => ""}
|
||||
{{ product.supplier.name }}
|
||||
|
||||
%td.notes{bindonce: ""} {{ product.notes | truncate:80 }}
|
||||
|
||||
%td{bindonce: ""}
|
||||
%span{"ng-hide" => "product.variants.length > 0"} {{ product.master.options_text }}
|
||||
%span{"ng-show" => "product.variants.length > 0"}
|
||||
%img.collapse{src: "/assets/collapse.png",
|
||||
"ng-show" => "product.show_variants",
|
||||
"ng-click" => "product.show_variants = !product.show_variants"}
|
||||
|
||||
%img.expand{src: "/assets/expand.png",
|
||||
"ng-show" => "!product.show_variants",
|
||||
"ng-click" => "product.show_variants = !product.show_variants"}
|
||||
%td
|
||||
%span{"ng-show" => "(product.variants.length == 0)"}
|
||||
%input{type: :number,
|
||||
value: nil,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
|
||||
name: "variants[{{product.master.id}}]",
|
||||
id: "variants_{{product.master.id}}",
|
||||
"ng-model" => "product.quantity"}
|
||||
|
||||
%td.group_buy
|
||||
%span{"ng-show" => "product.group_buy && (product.variants.length == 0)"}
|
||||
%input{type: :number,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
|
||||
name: "variant_attributes[{{product.master.id}}][max_quantity]",
|
||||
"ng-model" => "product.max_quantity"}
|
||||
|
||||
%td.price.text-right{bindonce: ""}
|
||||
%small{"ng-show" => "(product.variants.length > 0)"} from
|
||||
{{ productPrice(product) | currency }}
|
||||
|
||||
%tr.product-description{bindonce: ""}
|
||||
%td{colspan: 2}{{ product.notes | truncate:80 }}
|
||||
|
||||
%tr.variant{"ng-repeat" => "variant in product.variants", "ng-if" => "product.show_variants"}
|
||||
= render partial: "shop/variant"
|
||||
|
||||
%input.button.right{type: :submit, value: "Add to Cart"}
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
%td
|
||||
%td.notes
|
||||
%td{bindonce: ""} {{variant.options_text}}
|
||||
%td
|
||||
%input{type: :number,
|
||||
value: nil,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
|
||||
name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}",
|
||||
"ng-model" => "variant.quantity"}
|
||||
%td.group_buy
|
||||
%span{"ng-show" => "product.group_buy"}
|
||||
%input{type: :number,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
|
||||
name: "variant_attributes[{{variant.id}}][max_quantity]",
|
||||
"ng-model" => "variant.max_quantity"}
|
||||
%td.price.text-right{bindonce: ""}
|
||||
{{ variant.price | currency }}
|
||||
@@ -1,10 +1,6 @@
|
||||
collection @products
|
||||
attributes :id, :name, :permalink, :count_on_hand, :on_demand, :group_buy
|
||||
|
||||
node :show_variants do
|
||||
true
|
||||
end
|
||||
|
||||
node do |product|
|
||||
{
|
||||
notes: strip_tags(product.notes),
|
||||
@@ -17,6 +13,10 @@ child :supplier => :supplier do
|
||||
attributes :id, :name, :description
|
||||
end
|
||||
|
||||
child :primary_taxon => :primary_taxon do
|
||||
extends 'json/taxon'
|
||||
end
|
||||
|
||||
child :master => :master do
|
||||
attributes :id, :is_master, :count_on_hand, :options_text, :count_on_hand, :on_demand
|
||||
child :images => :images do
|
||||
|
||||
29
app/views/shop/products/_form.html.haml
Normal file
29
app/views/shop/products/_form.html.haml
Normal file
@@ -0,0 +1,29 @@
|
||||
%products.small-12.columns{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null",
|
||||
"infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1"}
|
||||
|
||||
= form_for :order, :url => populate_orders_path, html: {:class => "custom"} do
|
||||
|
||||
.row
|
||||
.small-6.columns
|
||||
%input#search.text{"ng-model" => "query",
|
||||
placeholder: "Search",
|
||||
"ng-debounce" => "150",
|
||||
"ofn-disable-enter" => true}
|
||||
.small-6.columns
|
||||
%input.button.right{type: :submit, value: "Add to Cart"}
|
||||
|
||||
%div{bindonce: true}
|
||||
%product{"ng-controller" => "ProductNodeCtrl",
|
||||
"ng-repeat" => "product in data.products | filter:query | orderBy:ordering.order | limitTo: limit track by product.id"}
|
||||
%div
|
||||
= render partial: "shop/products/summary"
|
||||
|
||||
%div{"bo-if" => "hasVariants"}
|
||||
= render partial: "shop/products/variants"
|
||||
|
||||
.variant.row{"bo-if" => "!hasVariants"}
|
||||
= render partial: "shop/products/master"
|
||||
|
||||
.row
|
||||
.small-12.columns
|
||||
%input.button.right.add_to_cart{type: :submit, value: "Add to Cart"}
|
||||
39
app/views/shop/products/_master.html.haml
Normal file
39
app/views/shop/products/_master.html.haml
Normal file
@@ -0,0 +1,39 @@
|
||||
.small-1.column
|
||||
%span.bulk{"bo-if" => "product.group_buy"} bulk
|
||||
|
||||
|
||||
.small-4.columns
|
||||
({{ product.master.options_text }})
|
||||
|
||||
-# WITHOUT GROUP BUY
|
||||
.small-5.columns{"bo-if" => "!product.group_buy"}
|
||||
%input{type: :number,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
|
||||
name: "variants[{{product.master.id}}]",
|
||||
id: "variants_{{product.master.id}}",
|
||||
"ng-model" => "product.quantity"}
|
||||
|
||||
-# WITH GROUP BUY
|
||||
.small-2.columns{"bo-if" => "product.group_buy"}
|
||||
%input{type: :number,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
|
||||
name: "variants[{{product.master.id}}]",
|
||||
id: "variants_{{product.master.id}}",
|
||||
"ng-model" => "product.quantity"}
|
||||
(min)
|
||||
|
||||
.small-3.columns{"bo-if" => "product.group_buy"}
|
||||
%input{type: :number,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
|
||||
name: "variant_attributes[{{product.master.id}}][max_quantity]",
|
||||
"ng-model" => "product.max_quantity"}
|
||||
(max)
|
||||
|
||||
.small-2.columns.text-right
|
||||
{{ product.price | currency }}
|
||||
2
app/views/shop/products/_modal.html.haml
Normal file
2
app/views/shop/products/_modal.html.haml
Normal file
@@ -0,0 +1,2 @@
|
||||
%ofn-modal{title: "{{product.name}}"}
|
||||
{{ product.description }}
|
||||
18
app/views/shop/products/_summary.html.haml
Normal file
18
app/views/shop/products/_summary.html.haml
Normal file
@@ -0,0 +1,18 @@
|
||||
.row.summary
|
||||
.small-1.column
|
||||
%img{"bo-src" => "product.master.images[0].small_url"}
|
||||
|
||||
.small-4.columns.summary-header
|
||||
%img{"bo-src" => "product.primary_taxon.icon",
|
||||
"ng-click" => "ordering.order = 'primary_taxon.name'",
|
||||
name: "{{product.primary_taxon.name}}"}
|
||||
{{ product.name}}
|
||||
-#= render partial: "shop/products/modal"
|
||||
|
||||
.small-5.columns.summary-header
|
||||
{{ product.supplier.name }}
|
||||
|
||||
.small-2.columns.summary-price.text-right.price
|
||||
%span{"ng-if" => "hasVariants"}
|
||||
%em from
|
||||
{{ price() | currency }}
|
||||
42
app/views/shop/products/_variants.html.haml
Normal file
42
app/views/shop/products/_variants.html.haml
Normal file
@@ -0,0 +1,42 @@
|
||||
.row.variants{bindonce: true,
|
||||
"ng-repeat" => "variant in product.variants"}
|
||||
|
||||
.small-1.column
|
||||
%span.bulk{"bo-if" => "product.group_buy"} bulk
|
||||
|
||||
|
||||
.small-4.columns
|
||||
{{ variant.options_text }}
|
||||
|
||||
-# WITHOUT GROUP BUY
|
||||
.small-5.columns{"bo-if" => "!product.group_buy"}
|
||||
%input{type: :number,
|
||||
value: nil,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
|
||||
name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}",
|
||||
"bo-model" => "variant.quantity"}
|
||||
|
||||
-# WITH GROUP BUY
|
||||
.small-2.columns{"bo-if" => "product.group_buy"}
|
||||
%input{type: :number,
|
||||
value: nil,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
|
||||
name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}",
|
||||
"bo-model" => "variant.quantity"}
|
||||
(min)
|
||||
|
||||
.small-3.columns{"bo-if" => "product.group_buy"}
|
||||
%input{type: :number,
|
||||
min: 0,
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
|
||||
name: "variant_attributes[{{variant.id}}][max_quantity]",
|
||||
"ng-model" => "variant.max_quantity"}
|
||||
(max)
|
||||
|
||||
.small-2.columns.text-right.price
|
||||
{{ variant.price | currency }}
|
||||
@@ -13,5 +13,5 @@
|
||||
|
||||
= render partial: "shopping_shared/details"
|
||||
|
||||
%products.row
|
||||
= render partial: "shop/products"
|
||||
.row
|
||||
= render partial: "shop/products/form"
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
Openfoodnetwork::Application.routes.draw do
|
||||
root :to => 'home#index'
|
||||
|
||||
get "/#/login", to: "home#index", as: :spree_login
|
||||
|
||||
resource :shop, controller: "shop" do
|
||||
get :products
|
||||
post :order_cycle
|
||||
get :order_cycle
|
||||
end
|
||||
|
||||
resources :groups
|
||||
resources :producers
|
||||
|
||||
get '/checkout', :to => 'checkout#edit' , :as => :checkout
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
class AddAttachmentPromoImageToEnterpriseGroup < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :enterprise_groups, :promo_image_file_name, :string
|
||||
add_column :enterprise_groups, :promo_image_content_type, :string
|
||||
add_column :enterprise_groups, :promo_image_file_size, :integer
|
||||
add_column :enterprise_groups, :promo_image_updated_at, :datetime
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :enterprise_groups, :promo_image_file_name
|
||||
remove_column :enterprise_groups, :promo_image_content_type
|
||||
remove_column :enterprise_groups, :promo_image_file_size
|
||||
remove_column :enterprise_groups, :promo_image_updated_at
|
||||
end
|
||||
end
|
||||
6
db/migrate/20140516044750_add_fields_to_groups.rb
Normal file
6
db/migrate/20140516044750_add_fields_to_groups.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class AddFieldsToGroups < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :enterprise_groups, :description, :text
|
||||
add_column :enterprise_groups, :long_description, :text
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
class AddAttachmentLogoToEnterpriseGroup < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :enterprise_groups, :logo_file_name, :string
|
||||
add_column :enterprise_groups, :logo_content_type, :string
|
||||
add_column :enterprise_groups, :logo_file_size, :integer
|
||||
add_column :enterprise_groups, :logo_updated_at, :datetime
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :enterprise_groups, :logo_file_name
|
||||
remove_column :enterprise_groups, :logo_content_type
|
||||
remove_column :enterprise_groups, :logo_file_size
|
||||
remove_column :enterprise_groups, :logo_updated_at
|
||||
end
|
||||
end
|
||||
18
db/schema.rb
18
db/schema.rb
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20140514044959) do
|
||||
ActiveRecord::Schema.define(:version => 20140516045323) do
|
||||
|
||||
create_table "adjustment_metadata", :force => true do |t|
|
||||
t.integer "adjustment_id"
|
||||
@@ -182,9 +182,19 @@ ActiveRecord::Schema.define(:version => 20140514044959) do
|
||||
add_index "enterprise_fees", ["enterprise_id"], :name => "index_enterprise_fees_on_enterprise_id"
|
||||
|
||||
create_table "enterprise_groups", :force => true do |t|
|
||||
t.string "name"
|
||||
t.boolean "on_front_page"
|
||||
t.integer "position"
|
||||
t.string "name"
|
||||
t.boolean "on_front_page"
|
||||
t.integer "position"
|
||||
t.string "promo_image_file_name"
|
||||
t.string "promo_image_content_type"
|
||||
t.integer "promo_image_file_size"
|
||||
t.datetime "promo_image_updated_at"
|
||||
t.text "description"
|
||||
t.text "long_description"
|
||||
t.string "logo_file_name"
|
||||
t.string "logo_content_type"
|
||||
t.integer "logo_file_size"
|
||||
t.datetime "logo_updated_at"
|
||||
end
|
||||
|
||||
create_table "enterprise_groups_enterprises", :id => false, :force => true do |t|
|
||||
|
||||
9
spec/controllers/groups_controller_spec.rb
Normal file
9
spec/controllers/groups_controller_spec.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe GroupsController do
|
||||
it "gets all visible groups" do
|
||||
EnterpriseGroup.stub_chain :on_front_page, :by_position
|
||||
EnterpriseGroup.should_receive :on_front_page
|
||||
get :index
|
||||
end
|
||||
end
|
||||
@@ -111,90 +111,6 @@ describe ShopController do
|
||||
response.body.should be_empty
|
||||
end
|
||||
|
||||
# TODO: this should be a controller test baby
|
||||
pending "filtering products" do
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:oc1) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), orders_close_at: 2.days.from_now) }
|
||||
let(:p1) { create(:simple_product, on_demand: false) }
|
||||
let(:p2) { create(:simple_product, on_demand: true) }
|
||||
let(:p3) { create(:simple_product, on_demand: false) }
|
||||
let(:p4) { create(:simple_product, on_demand: false) }
|
||||
let(:p5) { create(:simple_product, on_demand: false) }
|
||||
let(:p6) { create(:simple_product, on_demand: false) }
|
||||
let(:p7) { create(:simple_product, on_demand: false) }
|
||||
let(:v1) { create(:variant, product: p4, unit_value: 2) }
|
||||
let(:v2) { create(:variant, product: p4, unit_value: 3, on_demand: false) }
|
||||
let(:v3) { create(:variant, product: p4, unit_value: 4, on_demand: true) }
|
||||
let(:v4) { create(:variant, product: p5) }
|
||||
let(:v5) { create(:variant, product: p5) }
|
||||
let(:v6) { create(:variant, product: p7) }
|
||||
let(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) }
|
||||
|
||||
before do
|
||||
p1.master.count_on_hand = 1
|
||||
p2.master.count_on_hand = 0
|
||||
p1.master.update_attribute(:count_on_hand, 1)
|
||||
p2.master.update_attribute(:count_on_hand, 0)
|
||||
p3.master.update_attribute(:count_on_hand, 0)
|
||||
p6.master.update_attribute(:count_on_hand, 1)
|
||||
p6.delete
|
||||
p7.master.update_attribute(:count_on_hand, 1)
|
||||
v1.update_attribute(:count_on_hand, 1)
|
||||
v2.update_attribute(:count_on_hand, 0)
|
||||
v3.update_attribute(:count_on_hand, 0)
|
||||
v4.update_attribute(:count_on_hand, 1)
|
||||
v5.update_attribute(:count_on_hand, 0)
|
||||
v6.update_attribute(:count_on_hand, 1)
|
||||
v6.update_attribute(:deleted_at, Time.now)
|
||||
exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id)
|
||||
exchange.update_attribute :pickup_time, "frogs"
|
||||
exchange.variants << p1.master
|
||||
exchange.variants << p2.master
|
||||
exchange.variants << p3.master
|
||||
exchange.variants << p6.master
|
||||
exchange.variants << v1
|
||||
exchange.variants << v2
|
||||
exchange.variants << v3
|
||||
# v4 is in stock but not in distribution
|
||||
# v5 is out of stock and in the distribution
|
||||
# Neither should display, nor should their product, p5
|
||||
exchange.variants << v5
|
||||
exchange.variants << v6
|
||||
|
||||
controller.stub(:current_order).and_return order
|
||||
visit shop_path
|
||||
end
|
||||
|
||||
it "filters products based on availability" do
|
||||
# It shows on hand products
|
||||
page.should have_content p1.name
|
||||
page.should have_content p4.name
|
||||
|
||||
# It shows on demand products
|
||||
page.should have_content p2.name
|
||||
|
||||
# It does not show products that are neither on hand or on demand
|
||||
page.should_not have_content p3.name
|
||||
|
||||
# It shows on demand variants
|
||||
page.should have_content v3.options_text
|
||||
|
||||
# It does not show variants that are neither on hand or on demand
|
||||
page.should_not have_content v2.options_text
|
||||
|
||||
# It does not show products that have no available variants in this distribution
|
||||
page.should_not have_content p5.name
|
||||
|
||||
# It does not show deleted products
|
||||
page.should_not have_content p6.name
|
||||
|
||||
# It does not show deleted variants
|
||||
page.should_not have_content v6.name
|
||||
page.should_not have_content p7.name
|
||||
end
|
||||
end
|
||||
|
||||
context "RABL tests" do
|
||||
render_views
|
||||
before do
|
||||
@@ -211,6 +127,7 @@ describe ShopController do
|
||||
xhr :get, :products
|
||||
response.body.should_not have_content product.name
|
||||
end
|
||||
|
||||
it "strips html from description" do
|
||||
product.update_attribute(:description, "<a href='44'>turtles</a> frogs")
|
||||
xhr :get, :products
|
||||
@@ -223,6 +140,14 @@ describe ShopController do
|
||||
xhr :get, :products
|
||||
response.body.should have_content "998.0"
|
||||
end
|
||||
|
||||
it "includes the primary taxon" do
|
||||
taxon = mock_model(Spree::Taxon, name: "fruitbat")
|
||||
Spree::Product.any_instance.stub(:primary_taxon).and_return taxon
|
||||
taxon.stub_chain(:icon, :url).and_return ""
|
||||
xhr :get, :products
|
||||
response.body.should have_content "fruitbat"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -98,6 +98,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :enterprise_group, :class => EnterpriseGroup do
|
||||
name 'Enterprise group'
|
||||
description 'this is a group'
|
||||
on_front_page false
|
||||
end
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ feature %q{
|
||||
click_link 'New Enterprise Group'
|
||||
|
||||
fill_in 'enterprise_group_name', with: 'EGEGEG'
|
||||
fill_in 'enterprise_group_description', with: 'This is a description'
|
||||
check 'enterprise_group_on_front_page'
|
||||
select e1.name, from: 'enterprise_group_enterprise_ids'
|
||||
select e2.name, from: 'enterprise_group_enterprise_ids'
|
||||
@@ -42,6 +43,7 @@ feature %q{
|
||||
|
||||
eg = EnterpriseGroup.last
|
||||
eg.name.should == 'EGEGEG'
|
||||
eg.description.should == 'This is a description'
|
||||
eg.on_front_page.should be_true
|
||||
eg.enterprises.sort.should == [e1, e2].sort
|
||||
end
|
||||
@@ -62,6 +64,7 @@ feature %q{
|
||||
fill_in 'enterprise_group_name', with: 'xyzzy'
|
||||
uncheck 'enterprise_group_on_front_page'
|
||||
unselect e1.name, from: 'enterprise_group_enterprise_ids'
|
||||
|
||||
select e2.name, from: 'enterprise_group_enterprise_ids'
|
||||
click_button 'Update'
|
||||
|
||||
@@ -99,7 +102,6 @@ feature %q{
|
||||
EnterpriseGroup.all.should_not include eg
|
||||
end
|
||||
|
||||
|
||||
context "as an enterprise user" do
|
||||
xit "should show me only enterprises I manage when creating a new enterprise group"
|
||||
end
|
||||
|
||||
@@ -90,10 +90,6 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
it "should not show quantity field for product with variants" do
|
||||
visit shop_path
|
||||
page.should_not have_selector("#variants_#{product.master.id}", visible: true)
|
||||
|
||||
#it "expands variants" do
|
||||
find(".collapse").trigger "click"
|
||||
page.should_not have_text variant1.options_text
|
||||
end
|
||||
|
||||
it "uses the adjusted price" do
|
||||
@@ -104,15 +100,16 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
visit shop_path
|
||||
|
||||
# Page should not have product.price (with or without fee)
|
||||
page.should_not have_selector 'tr.product > td', text: "from $10.00"
|
||||
page.should_not have_selector 'tr.product > td', text: "from $33.00"
|
||||
page.should_not have_price "from $10.00"
|
||||
page.should_not have_price "from $33.00"
|
||||
|
||||
# Page should have variant prices (with fee)
|
||||
page.should have_selector 'tr.variant > td.price', text: "$43.00"
|
||||
page.should have_selector 'tr.variant > td.price', text: "$53.00"
|
||||
page.should have_price "$43.00"
|
||||
page.should have_price "$53.00"
|
||||
|
||||
# Product price should be listed as the lesser of these
|
||||
page.should have_selector 'tr.product > td', text: "from $43.00"
|
||||
#page.should have_selector 'tr.product > td', text: "from $43.00"
|
||||
page.should have_price "from $43.00"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -131,7 +128,7 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
it "should save group buy data to ze cart" do
|
||||
fill_in "variants[#{product.master.id}]", with: 5
|
||||
fill_in "variant_attributes[#{product.master.id}][max_quantity]", with: 9
|
||||
first("form.custom > input.button.right").click
|
||||
add_to_cart
|
||||
page.should have_content product.name
|
||||
li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last
|
||||
li.max_quantity.should == 9
|
||||
@@ -142,7 +139,7 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
pending "adding a product with a max quantity less than quantity results in max_quantity==quantity" do
|
||||
fill_in "variants[#{product.master.id}]", with: 5
|
||||
fill_in "variant_attributes[#{product.master.id}][max_quantity]", with: 1
|
||||
first("form.custom > input.button.right").click
|
||||
add_to_cart
|
||||
page.should have_content product.name
|
||||
li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last
|
||||
li.max_quantity.should == 5
|
||||
@@ -161,7 +158,7 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
it "should save group buy data to ze cart" do
|
||||
fill_in "variants[#{variant.id}]", with: 6
|
||||
fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 7
|
||||
first("form.custom > input.button.right").click
|
||||
add_to_cart
|
||||
page.should have_content product.name
|
||||
li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last
|
||||
li.max_quantity.should == 7
|
||||
@@ -181,7 +178,7 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
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
|
||||
add_to_cart
|
||||
current_path.should == "/cart"
|
||||
page.should have_content product.name
|
||||
end
|
||||
|
||||
15
spec/helpers/groups_helper_spec.rb
Normal file
15
spec/helpers/groups_helper_spec.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
require 'spec_helper'
|
||||
|
||||
# Specs in this file have access to a helper object that includes
|
||||
# the GroupsHelper. For example:
|
||||
#
|
||||
# describe GroupsHelper do
|
||||
# describe "string concat" do
|
||||
# it "concats two strings with spaces" do
|
||||
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
describe GroupsHelper do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
@@ -11,6 +11,13 @@ describe EnterpriseGroup do
|
||||
e = build(:enterprise_group, name: '')
|
||||
e.should_not be_valid
|
||||
end
|
||||
|
||||
it "requires a description" do
|
||||
e = build(:enterprise_group, description: '')
|
||||
end
|
||||
|
||||
it { should have_attached_file :promo_image }
|
||||
it { should have_attached_file :logo }
|
||||
end
|
||||
|
||||
describe "relations" do
|
||||
|
||||
@@ -547,5 +547,15 @@ module Spree
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Taxons" do
|
||||
let(:taxon1) { create(:taxon) }
|
||||
let(:taxon2) { create(:taxon) }
|
||||
let(:product) { create(:simple_product, taxons: [taxon1, taxon2]) }
|
||||
|
||||
it "returns the first taxon as the primary taxon" do
|
||||
product.primary_taxon.should == taxon1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
module ShopWorkflow
|
||||
def add_to_cart
|
||||
first("input.add_to_cart").click
|
||||
end
|
||||
|
||||
def have_price(price)
|
||||
have_selector ".price", text: price
|
||||
end
|
||||
|
||||
def set_order(order)
|
||||
ApplicationController.any_instance.stub(:session).and_return({order_id: order.id, access_token: order.token})
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user