diff --git a/.rspec b/.rspec index 53607ea52b..333fedd86c 100644 --- a/.rspec +++ b/.rspec @@ -1 +1,2 @@ --colour +--profile diff --git a/Gemfile b/Gemfile index 6d9b351cca..dec618a871 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,7 @@ gem 'bugsnag' gem 'newrelic_rpm' gem 'haml' gem 'sass', "~> 3.2" +gem 'sass-rails', '~> 3.2.3', groups: [:default, :assets] gem 'aws-sdk' gem 'db2fog' gem 'andand' @@ -39,7 +40,6 @@ gem 'rack-ssl', :require => 'rack/ssl' # Gems used only for assets and not required # in production environments by default. group :assets do - gem 'sass-rails', '~> 3.2.3' gem 'compass-rails' gem 'coffee-rails', '~> 3.2.1' @@ -49,6 +49,7 @@ group :assets do gem 'uglifier', '>= 1.0.3' gem 'turbo-sprockets-rails3' + gem 'foundation-icons-sass-rails' end gem "foundation-rails" diff --git a/Gemfile.lock b/Gemfile.lock index bdd90bf6b8..971e536da5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -262,6 +262,9 @@ GEM nokogiri (~> 1.5) ruby-hmac formatador (0.2.4) + foundation-icons-sass-rails (3.0.0) + railties (>= 3.1.1) + sass-rails (>= 3.1.1) foundation-rails (5.2.1.0) railties (>= 3.1.0) sass (>= 3.2.0) @@ -509,6 +512,7 @@ DEPENDENCIES eaterprises_feature! factory_girl_rails faker + foundation-icons-sass-rails foundation-rails foundation_rails_helper! geocoder diff --git a/README.markdown b/README.markdown index 77b56784e6..4f6013a317 100644 --- a/README.markdown +++ b/README.markdown @@ -1,3 +1,6 @@ +[](http://ci.openfood.com.au:8080/job/openfoodweb%20-%20tests/) +[](https://codeclimate.com/github/openfoodfoundation/openfoodnetwork) + # Open Food Network Connect suppliers (ie. farmers), distributors (ie. co-ops) and diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 9589b790b9..5b018e6548 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -9,9 +9,15 @@ angular.module('order_cycle', ['ngResource']) $scope.loaded = -> Enterprise.loaded && EnterpriseFee.loaded + $scope.suppliedVariants = (enterprise_id) -> + Enterprise.suppliedVariants(enterprise_id) + $scope.exchangeSelectedVariants = (exchange) -> OrderCycle.exchangeSelectedVariants(exchange) + $scope.setExchangeVariants = (exchange, variants, selected) -> + OrderCycle.setExchangeVariants(exchange, variants, selected) + $scope.enterpriseTotalVariants = (enterprise) -> Enterprise.totalVariants(enterprise) @@ -83,9 +89,15 @@ angular.module('order_cycle', ['ngResource']) $scope.loaded = -> Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded + $scope.suppliedVariants = (enterprise_id) -> + Enterprise.suppliedVariants(enterprise_id) + $scope.exchangeSelectedVariants = (exchange) -> OrderCycle.exchangeSelectedVariants(exchange) + $scope.setExchangeVariants = (exchange, variants, selected) -> + OrderCycle.setExchangeVariants(exchange, variants, selected) + $scope.enterpriseTotalVariants = (enterprise) -> Enterprise.totalVariants(enterprise) @@ -175,6 +187,9 @@ angular.module('order_cycle', ['ngResource']) toggleProducts: (exchange) -> exchange.showProducts = !exchange.showProducts + setExchangeVariants: (exchange, variants, selected) -> + exchange.variants[variant] = selected for variant in variants + addSupplier: (new_supplier_id) -> this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []}) @@ -271,12 +286,27 @@ angular.module('order_cycle', ['ngResource']) console.log('Failed to update order cycle') dataForSubmit: -> - data = angular.extend({}, this.order_cycle) + data = this.deepCopy() data = this.removeInactiveExchanges(data) data = this.translateCoordinatorFees(data) data = this.translateExchangeFees(data) data + deepCopy: -> + data = angular.extend({}, this.order_cycle) + + # Copy exchanges + data.incoming_exchanges = (angular.extend {}, exchange for exchange in this.order_cycle.incoming_exchanges) if this.order_cycle.incoming_exchanges? + data.outgoing_exchanges = (angular.extend {}, exchange for exchange in this.order_cycle.outgoing_exchanges) if this.order_cycle.outgoing_exchanges? + + # Copy exchange fees + all_exchanges = (data.incoming_exchanges || []) + (data.outgoing_exchanges || []) + for exchange in all_exchanges + if exchange.enterprise_fees? + exchange.enterprise_fees = (angular.extend {}, fee for fee in exchange.enterprise_fees) + + data + removeInactiveExchanges: (order_cycle) -> order_cycle.incoming_exchanges = (exchange for exchange in order_cycle.incoming_exchanges when exchange.active) @@ -322,6 +352,16 @@ angular.module('order_cycle', ['ngResource']) this.enterprises + suppliedVariants: (enterprise_id) -> + vs = (this.variantsOf(product) for product in this.enterprises[enterprise_id].supplied_products) + [].concat vs... + + variantsOf: (product) -> + if product.variants.length > 0 + variant.id for variant in product.variants + else + [product.master_id] + totalVariants: (enterprise) -> numVariants = 0 diff --git a/app/assets/javascripts/darkswarm/controllers/account_sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/account_sidebar_controller.js.coffee new file mode 100644 index 0000000000..b7180fab49 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/account_sidebar_controller.js.coffee @@ -0,0 +1,16 @@ +window.AccountSidebarCtrl = Darkswarm.controller "AccountSidebarCtrl", ($scope, $http, $location, SpreeUser, Navigation) -> + $scope.path = "/account" + Navigation.paths.push $scope.path + + $scope.active = -> + $location.path() == $scope.path + + $scope.select = -> + Navigation.navigate($scope.path) + + $scope.emptyCart = (href, ev)-> + console.log href + if $(ev.delegateTarget).hasClass "empties-cart" + location.href = href if confirm "Changing your Hub will clear your cart." + else + location.href = href diff --git a/app/assets/javascripts/darkswarm/controllers/forgot_sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/forgot_sidebar_controller.js.coffee index f0e708f2f7..e3c272a3af 100644 --- a/app/assets/javascripts/darkswarm/controllers/forgot_sidebar_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/forgot_sidebar_controller.js.coffee @@ -1,12 +1,14 @@ -window.ForgotSidebarCtrl = Darkswarm.controller "ForgotSidebarCtrl", ($scope, $http, $location, SpreeUser) -> +window.ForgotSidebarCtrl = Darkswarm.controller "ForgotSidebarCtrl", ($scope, $http, $location, SpreeUser, Navigation) -> $scope.spree_user = SpreeUser.spree_user + $scope.path = "/forgot" $scope.sent = false + Navigation.paths.push $scope.path $scope.active = -> - $location.path() == '/forgot' + $location.path() == $scope.path $scope.select = -> - $location.path("/forgot") + Navigation.navigate($scope.path) $scope.submit = -> if $scope.spree_user.email != null diff --git a/app/assets/javascripts/darkswarm/controllers/login_sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/login_sidebar_controller.js.coffee index e3d0d20d25..54b10b0b4a 100644 --- a/app/assets/javascripts/darkswarm/controllers/login_sidebar_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/login_sidebar_controller.js.coffee @@ -1,11 +1,14 @@ -window.LoginSidebarCtrl = Darkswarm.controller "LoginSidebarCtrl", ($scope, $http, $location, SpreeUser) -> +window.LoginSidebarCtrl = Darkswarm.controller "LoginSidebarCtrl", ($scope, $http, $location, SpreeUser, Navigation) -> $scope.spree_user = SpreeUser.spree_user + $scope.path = "/login" + Navigation.paths.push $scope.path $scope.active = -> - $location.path() == '/login' + $location.path() == $scope.path $scope.select = -> - $location.path("/login") + Navigation.navigate($scope.path) + $scope.submit = -> $http.post("/user/spree_user/sign_in", {spree_user: $scope.spree_user}).success (data)-> diff --git a/app/assets/javascripts/darkswarm/controllers/menu_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/menu_controller.js.coffee index cd5a4ce5fc..889c8fa8f4 100644 --- a/app/assets/javascripts/darkswarm/controllers/menu_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/menu_controller.js.coffee @@ -1,12 +1,13 @@ -window.MenuCtrl = Darkswarm.controller "MenuCtrl", ($scope, $location) -> +window.MenuCtrl = Darkswarm.controller "MenuCtrl", ($scope, Navigation) -> + $scope.toggleLogin = -> - if $location.path() == "/login" - $location.path("/") - else - $location.path("login") + Navigation.navigate "/login" $scope.toggleSignup = -> - if $location.path() == "/signup" - $location.path("/") - else - $location.path("signup") + Navigation.navigate "/signup" + + $scope.toggleSignup = -> + Navigation.navigate "/signup" + + $scope.toggle = (path = null)-> + Navigation.navigate(path) diff --git a/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee index fc6413705a..29e81ae861 100644 --- a/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee @@ -1,5 +1,15 @@ -Darkswarm.controller "OrderCycleCtrl", ($scope, $rootScope, OrderCycle) -> +Darkswarm.controller "OrderCycleCtrl", ($scope, $rootScope, OrderCycle, $timeout) -> $scope.order_cycle = OrderCycle.order_cycle $scope.OrderCycle = OrderCycle + $scope.changeOrderCycle = -> OrderCycle.push_order_cycle() + $timeout -> + $("#order_cycle_id").trigger("closeTrigger") + + # Timeout forces this to be evaluated after everything is loaded + # This is a hack. We should probably write our own "popover" directive + # That takes an expression instead of a trigger, and binds to that + $timeout => + if !$scope.OrderCycle.selected() + $("#order_cycle_id").trigger("openTrigger") diff --git a/app/assets/javascripts/darkswarm/controllers/sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/sidebar_controller.js.coffee index 6f0b954116..8544f57922 100644 --- a/app/assets/javascripts/darkswarm/controllers/sidebar_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/sidebar_controller.js.coffee @@ -1,3 +1,5 @@ window.SidebarCtrl = Darkswarm.controller "SidebarCtrl", ($scope, $location) -> + $scope.sidebarPaths = ["/login", "/signup", "/forgot", "/account"] + $scope.active = -> - $location.path() in ["/login", "/signup", "/forgot"] + $location.path() in $scope.sidebarPaths diff --git a/app/assets/javascripts/darkswarm/controllers/signup_sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/signup_sidebar_controller.js.coffee index 5c1c80ce23..14c924e275 100644 --- a/app/assets/javascripts/darkswarm/controllers/signup_sidebar_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/signup_sidebar_controller.js.coffee @@ -1,14 +1,16 @@ -window.SignupSidebarCtrl = Darkswarm.controller "SignupSidebarCtrl", ($scope, $http, $location, SpreeUser) -> +window.SignupSidebarCtrl = Darkswarm.controller "SignupSidebarCtrl", ($scope, $http, $location, SpreeUser, Navigation) -> $scope.spree_user = SpreeUser.spree_user + $scope.path = "/signup" + Navigation.paths.push $scope.path $scope.errors = email: null password: null $scope.active = -> - $location.path() == '/signup' + $location.path() == $scope.path $scope.select = -> - $location.path("/signup") + Navigation.navigate($scope.path) $scope.submit = -> $http.post("/user/spree_user", {spree_user: $scope.spree_user}).success (data)-> diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index bffcc780e3..6be3a4a155 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -1,4 +1,7 @@ -window.Darkswarm = angular.module("Darkswarm", ["ngResource", "filters", 'mm.foundation']).config ($httpProvider) -> +window.Darkswarm = angular.module("Darkswarm", ["ngResource", "filters", 'mm.foundation']).config ($httpProvider, $tooltipProvider) -> $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest' $httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*" + + # This allows us to trigger these two events on tooltips + $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ) diff --git a/app/assets/javascripts/darkswarm/services/navigation.js.coffee b/app/assets/javascripts/darkswarm/services/navigation.js.coffee new file mode 100644 index 0000000000..a89523dbff --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/navigation.js.coffee @@ -0,0 +1,12 @@ +Darkswarm.factory 'Navigation', ($location) -> + new class Navigation + paths: [] + path: null + + navigate: (path = false)-> + @path = path || @path || @paths[0] + + if $location.path() == @path + $location.path("/") + else + $location.path(@path) diff --git a/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee b/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee index 58d4d11946..c7a4473b03 100644 --- a/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee +++ b/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee @@ -7,9 +7,8 @@ Darkswarm.factory 'OrderCycle', ($resource, Product, orderCycleData) -> Product.update() @orders_close_at: -> - if @order_cycle + if @selected() @order_cycle.orders_close_at - else - "" + @selected: -> - @order_cycle != null + @order_cycle != null and !$.isEmptyObject(@order_cycle) and @order_cycle.orders_close_at != undefined diff --git a/app/assets/javascripts/darkswarm/services/product.js.coffee b/app/assets/javascripts/darkswarm/services/product.js.coffee index 4140da20fa..89778b659c 100644 --- a/app/assets/javascripts/darkswarm/services/product.js.coffee +++ b/app/assets/javascripts/darkswarm/services/product.js.coffee @@ -2,10 +2,11 @@ Darkswarm.factory 'Product', ($resource) -> new class Product data: { products: null + loading: true } update: -> @data.products = $resource("/shop/products").query => - #console.log @products + @data.loading = false @data all: -> @data.products || @update() diff --git a/app/assets/javascripts/shared/mm-foundation-tpls-0.2.0-SNAPSHOT.js b/app/assets/javascripts/shared/mm-foundation-tpls-0.2.0-SNAPSHOT.js index fb1217f780..d4fb9527d8 100644 --- a/app/assets/javascripts/shared/mm-foundation-tpls-0.2.0-SNAPSHOT.js +++ b/app/assets/javascripts/shared/mm-foundation-tpls-0.2.0-SNAPSHOT.js @@ -2058,7 +2058,7 @@ angular.module( 'mm.foundation.tour', [ 'mm.foundation.position', 'mm.foundation }; }]) -.directive( 'stepText', [ '$position', '$tooltip', '$tour', '$window', function ( $position, $tooltip, $tour, $window ) { +.directive( 'step', [ '$position', '$tooltip', '$tour', '$window', function ( $position, $tooltip, $tour, $window ) { function isElementInViewport( element ) { var rect = element[0].getBoundingClientRect(); @@ -2092,6 +2092,7 @@ angular.module( 'mm.foundation.tour', [ 'mm.foundation.position', 'mm.foundation return $tooltip( 'step', 'step', show ); }]); + angular.module('mm.foundation.typeahead', ['mm.foundation.position', 'mm.foundation.bindHtml']) /** @@ -2602,7 +2603,7 @@ angular.module("template/tour/tour.html", []).run(["$templateCache", function($t "
\n" + " \n" + " Next\n" + - " End\n" + + //" Close\n" + " ×\n" + " \n" + "\n" + diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss index 8c8ddeb6b7..92092bd136 100644 --- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss +++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss @@ -52,6 +52,11 @@ form.order_cycle { border-bottom: 2px solid #C3D9FF; } + .exchange-select-all-variants { + clear: both; + margin: 5px; + } + .exchange-product { float: left; overflow: auto; diff --git a/app/assets/stylesheets/darkswarm/all.scss b/app/assets/stylesheets/darkswarm/all.scss index 44e8ea248c..7c3f9de2d5 100644 --- a/app/assets/stylesheets/darkswarm/all.scss +++ b/app/assets/stylesheets/darkswarm/all.scss @@ -7,3 +7,4 @@ *= require foundation *= require_tree . */ +@import 'foundation-icons'; diff --git a/app/assets/stylesheets/darkswarm/header.css.sass b/app/assets/stylesheets/darkswarm/header.css.sass index 5d1764b8e1..910c40c6bc 100644 --- a/app/assets/stylesheets/darkswarm/header.css.sass +++ b/app/assets/stylesheets/darkswarm/header.css.sass @@ -1,6 +1,20 @@ /*body { background: #ff0000; }*/ nav.top-bar margin-bottom: 0px + a.icon + &:hover + text-decoration: none + height: 45px + color: white + i + font-size: 29px + line-height: 45px + span + font-size: 13px + display: inline-block + line-height: 45px + height: 45px + vertical-align: top body > section[role='main'] padding: 0px diff --git a/app/assets/stylesheets/darkswarm/sidebar.css.sass b/app/assets/stylesheets/darkswarm/sidebar.css.sass index 47a3d8f3c1..e2c3b06d40 100644 --- a/app/assets/stylesheets/darkswarm/sidebar.css.sass +++ b/app/assets/stylesheets/darkswarm/sidebar.css.sass @@ -2,6 +2,8 @@ // We can't import foundation components? // See https://github.com/zurb/foundation/issues/3855#issuecomment-30372252 + +@import "foundation" @import "variables" @import "components/global" @import "components/buttons" @@ -20,3 +22,12 @@ .tabs dd a padding: 0.5em 1em + + #account + dl + @include clearfix + dt, dd + display: inline-block + + p > strong + display: block diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index 2e817150f5..be97bd604f 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -30,7 +30,6 @@ class Shop::CheckoutController < Spree::CheckoutController if @order.state == "complete" || @order.completed? flash.notice = t(:order_processed_successfully) - flash[:commerce_tracking] = "nothing special" respond_with(@order, :location => order_path(@order)) else clear_ship_address diff --git a/app/controllers/shop/shop_controller.rb b/app/controllers/shop/shop_controller.rb index 1af6ceec87..9db927cece 100644 --- a/app/controllers/shop/shop_controller.rb +++ b/app/controllers/shop/shop_controller.rb @@ -10,7 +10,7 @@ class Shop::ShopController < BaseController def products unless @products = current_order_cycle.andand .valid_products_distributed_by(current_distributor).andand - .select { |p| p.has_stock_for_distribution?(current_order_cycle, current_distributor) }.andand + .select { |p| !p.deleted? && p.has_stock_for_distribution?(current_order_cycle, current_distributor) }.andand .sort_by {|p| p.name } render json: "", status: 404 diff --git a/app/controllers/spree/users_controller_decorator.rb b/app/controllers/spree/users_controller_decorator.rb new file mode 100644 index 0000000000..5f0eaa9759 --- /dev/null +++ b/app/controllers/spree/users_controller_decorator.rb @@ -0,0 +1,3 @@ +Spree::UsersController.class_eval do + layout 'darkswarm' +end diff --git a/app/controllers/user_passwords_controller.rb b/app/controllers/user_passwords_controller.rb index 8e6f599523..c46d294457 100644 --- a/app/controllers/user_passwords_controller.rb +++ b/app/controllers/user_passwords_controller.rb @@ -1,4 +1,5 @@ class UserPasswordsController < Spree::UserPasswordsController + layout 'darkswarm' def create self.resource = resource_class.send_reset_password_instructions(params[resource_name]) diff --git a/app/helpers/shared_helper.rb b/app/helpers/shared_helper.rb index 5082d3dddb..f255999009 100644 --- a/app/helpers/shared_helper.rb +++ b/app/helpers/shared_helper.rb @@ -18,4 +18,8 @@ module SharedHelper [] end end + + def enterprise_user? + spree_current_user.andand.enterprises.count > 0 + end end diff --git a/app/helpers/spree/orders_helper.rb b/app/helpers/spree/orders_helper.rb index d8430f723f..86f295ae52 100644 --- a/app/helpers/spree/orders_helper.rb +++ b/app/helpers/spree/orders_helper.rb @@ -14,5 +14,13 @@ module Spree def alternative_available_distributors(order) DistributionChangeValidator.new(order).available_distributors(Enterprise.all) - [order.distributor] end + + def last_completed_order + spree_current_user.orders.complete.last + end + + def cart_count + current_order.andand.line_items.count || 0 + end end end diff --git a/app/mailers/spree/user_mailer_decorator.rb b/app/mailers/spree/user_mailer_decorator.rb new file mode 100644 index 0000000000..ff8bdc4691 --- /dev/null +++ b/app/mailers/spree/user_mailer_decorator.rb @@ -0,0 +1,8 @@ +Spree::UserMailer.class_eval do + + def signup_confirmation(user) + @user = user + mail(:to => user.email, :from => from_address, + :subject => 'Welcome to ' + Spree::Config[:site_name]) + end +end diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 243a311bcc..122586ba96 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -11,7 +11,7 @@ class Exchange < ActiveRecord::Base has_many :enterprise_fees, :through => :exchange_fees validates_presence_of :order_cycle, :sender, :receiver - validates_uniqueness_of :sender_id, :scope => [:order_cycle_id, :receiver_id] + validates_uniqueness_of :sender_id, :scope => [:order_cycle_id, :receiver_id, :incoming] accepts_nested_attributes_for :variants diff --git a/app/models/spree/adjustment_decorator.rb b/app/models/spree/adjustment_decorator.rb index 5c4748b227..2c5bb0be0a 100644 --- a/app/models/spree/adjustment_decorator.rb +++ b/app/models/spree/adjustment_decorator.rb @@ -1,6 +1,6 @@ module Spree Adjustment.class_eval do - has_one :metadata, class_name: 'AdjustmentMetadata' + has_one :metadata, class_name: 'AdjustmentMetadata', dependent: :destroy scope :enterprise_fee, where(originator_type: 'EnterpriseFee') end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 40c06c3132..ccc827ec57 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -85,6 +85,7 @@ Spree::Order.class_eval do def set_order_cycle!(order_cycle) self.order_cycle = order_cycle self.distributor = nil unless order_cycle.nil? || order_cycle.has_distributor?(distributor) + self.empty! save! end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 5acaff924e..9dbc8226f5 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -6,6 +6,7 @@ Spree.user_class.class_eval do accepts_nested_attributes_for :enterprise_roles, :allow_destroy => true attr_accessible :enterprise_ids, :enterprise_roles_attributes + after_create :send_signup_confirmation def build_enterprise_roles Enterprise.all.each do |enterprise| @@ -14,4 +15,8 @@ Spree.user_class.class_eval do end end end + + def send_signup_confirmation + Spree::UserMailer.signup_confirmation(self).deliver + end end diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 923dce4e11..4e86bbbca5 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -12,6 +12,7 @@ Spree::Variant.class_eval do if: -> v { v.product.variant_unit.present? && v.unit_value.nil? }, unless: :is_master + before_validation :update_weight_from_unit_value after_save :update_units scope :in_stock, where('spree_variants.count_on_hand > 0 OR spree_variants.on_demand=?', true) @@ -43,6 +44,10 @@ Spree::Variant.class_eval do private + def update_weight_from_unit_value + self.weight = unit_value / 1000 if self.product.variant_unit == 'weight' && unit_value.present? + end + def update_units delete_unit_option_values diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index 2d3c728bf0..1cb9707ecf 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -155,4 +155,4 @@ .omega.four.columns = image_tag @object.promo_image.url if @object.promo_image.present? - = f.file_field :pro_image + = f.file_field :promo_image diff --git a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml index 3d083eb2d7..3f7d7ba578 100644 --- a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml @@ -1,10 +1,17 @@ %td{:colspan => 3} + .exchange-select-all-variants + %label + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-click' => 'setExchangeVariants(exchange, incomingExchangesVariants(), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' + Select all + .exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle'} .exchange-product-details .supplier {{ product.supplier_name }} - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' - %img{'ng-src' => '{{ product.image_url }}'} - {{ product.name }} + %label + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants', 'ng-disabled' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' + %img{'ng-src' => '{{ product.image_url }}'} + {{ product.name }} .exchange-product-variant{'ng-repeat' => 'variant in product.variants | filter:variantSuppliedToOrderCycle'} - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}' - {{ variant.label }} + %label + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}' + {{ variant.label }} diff --git a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml index 4523bd8337..6ad13a88a6 100644 --- a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml @@ -1,18 +1,26 @@ / TODO: Unify this with exchange_distributed_products_form %td{:colspan => 3} + .exchange-select-all-variants + %label + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-click' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants' + Select all + .exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'} .exchange-product-details - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants', 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' - %img{'ng-src' => '{{ product.image_url }}'} - {{ product.name }} + %label + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants', 'ng-disabled' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' + %img{'ng-src' => '{{ product.image_url }}'} + {{ product.name }} -# When the master variant is in the order cycle but the product has variants, we want to -# be able to remove the master variant, since it serves no purpose. Display a checkbox to do so. .exchange-product-variant{'ng-show' => 'exchange.variants[product.master_id] && product.variants'} - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' - Obsolete master + %label + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' + Obsolete master .exchange-product-variant{'ng-repeat' => 'variant in product.variants'} - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'ofn-sync-distributions' => '{{ variant.id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}' - {{ variant.label }} + %label + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'ofn-sync-distributions' => '{{ variant.id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}' + {{ variant.label }} diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index e2c0dd3ea9..6eacb9f58f 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -31,7 +31,7 @@ %h2 Coordinator = f.label :coordinator_id, 'Coordinator' -= f.collection_select :coordinator_id, coordinating_enterprises, :id, :name, {}, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} += f.collection_select :coordinator_id, coordinating_enterprises, :id, :name, {include_blank: true}, {'ng-model' => 'order_cycle.coordinator_id', 'ofn-on-change' => 'order_cycle.coordinator_fees = []', 'required' => true} = render 'coordinator_fees', f: f diff --git a/app/views/admin/order_cycles/show.rep b/app/views/admin/order_cycles/show.rep index e5ccd66226..1f91cbe3e9 100644 --- a/app/views/admin/order_cycles/show.rep +++ b/app/views/admin/order_cycles/show.rep @@ -9,7 +9,7 @@ r.element :order_cycle, @order_cycle do r.element :id end - r.list_of :exchanges do |exchange| + r.list_of :exchanges, @order_cycle.exchanges.order('id ASC') do |exchange| r.element :id r.element :sender_id r.element :receiver_id diff --git a/app/views/layouts/_bugherd_script.html.haml b/app/views/layouts/_bugherd_script.html.haml index 326c48da00..ad6fe585f5 100644 --- a/app/views/layouts/_bugherd_script.html.haml +++ b/app/views/layouts/_bugherd_script.html.haml @@ -1,4 +1,4 @@ -- if Rails.env.staging? +- if Rails.env.staging? or Rails.env.production? :javascript (function (d, t) { var bh = d.createElement(t), s = d.getElementsByTagName(t)[0]; @@ -7,11 +7,12 @@ s.parentNode.insertBefore(bh, s); })(document, 'script'); -- elsif Rails.env.production? - :javascript - (function (d, t) { - var bh = d.createElement(t), s = d.getElementsByTagName(t)[0]; - bh.type = 'text/javascript'; - bh.src = '//www.bugherd.com/sidebarv2.js?apikey=xro3uv55objies58o2wrua'; - s.parentNode.insertBefore(bh, s); - })(document, 'script'); + +-#- elsif Rails.env.production? + -#:javascript + -#(function (d, t) { + -#var bh = d.createElement(t), s = d.getElementsByTagName(t)[0]; + -#bh.type = 'text/javascript'; + -#bh.src = '//www.bugherd.com/sidebarv2.js?apikey=xro3uv55objies58o2wrua'; + -#s.parentNode.insertBefore(bh, s); + -#})(document, 'script'); diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 048e8a9dc1..51aaca837d 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -22,6 +22,4 @@ = yield #footer - - = yield :scripts diff --git a/app/views/shared/_account_sidebar.html.haml b/app/views/shared/_account_sidebar.html.haml new file mode 100644 index 0000000000..884f6c06c6 --- /dev/null +++ b/app/views/shared/_account_sidebar.html.haml @@ -0,0 +1,20 @@ +#account{"ng-controller" => "AccountSidebarCtrl"} + .row + .panel + %p + %strong= link_to "Manage my account", account_path + - if enterprise_user? + %strong= link_to "Enterprise admin", admin_path + - if order = last_completed_order + %dl + %dt Current Hub: + %dd= link_to current_distributor.name, main_app.shop_path + %br + %dt Last hub: + %dd + - if order.distributor != current_distributor + = link_to "#{order.distributor.name}".html_safe, "", + {class: distributor_link_class(order.distributor), + "ng-click" => "emptyCart('#{main_app.shop_enterprise_path(order.distributor)}', $event)"} + - else + = order.distributor.name diff --git a/app/views/shared/_login_sidebar.html.haml b/app/views/shared/_login_sidebar.html.haml index b5b05ca245..3f7ca15b0d 100644 --- a/app/views/shared/_login_sidebar.html.haml +++ b/app/views/shared/_login_sidebar.html.haml @@ -3,8 +3,10 @@ active: "active()", select: "select()"} %form{"ng-submit" => "submit()"} - .alert-box.alert{"ng-show" => "errors != null"} - {{ errors }} + .row + .large-12.columns + .alert-box.alert{"ng-show" => "errors != null"} + {{ errors }} .row .large-12.columns %label{for: "email"} Email diff --git a/app/views/shared/_menu.html.haml b/app/views/shared/_menu.html.haml index fe0faf1143..3ece58cf4d 100644 --- a/app/views/shared/_menu.html.haml +++ b/app/views/shared/_menu.html.haml @@ -1,9 +1,21 @@ %nav.top-bar %section.top-bar-section %ul.left{"ng-controller" => "MenuCtrl"} + %li + %a.icon{"ng-click" => "toggle()"} + %i.fi-list %li= link_to image_tag("ofn_logo_small.png"), root_path %li.divider - if spree_current_user.nil? = render 'shared/signed_out' - else = render 'shared/signed_in' + + %section.top-bar-section + %ul.right + %li.cart + %a.icon{href: cart_url} + %i.fi-shopping-cart + %span + = cart_count + items diff --git a/app/views/shared/_sidebar.html.haml b/app/views/shared/_sidebar.html.haml index d4c96f4a33..04891a5c82 100644 --- a/app/views/shared/_sidebar.html.haml +++ b/app/views/shared/_sidebar.html.haml @@ -1,7 +1,12 @@ %section#sidebar{ role: "complementary", "ng-controller" => "SidebarCtrl", "ng-class" => "{'active' : active()}"} - %tabset - = render partial: "shared/login_sidebar" - = render partial: "shared/signup_sidebar" - = render partial: "shared/forgot_sidebar" + + - if spree_current_user.nil? + %tabset + = render partial: "shared/login_sidebar" + = render partial: "shared/signup_sidebar" + = render partial: "shared/forgot_sidebar" + - else + = render partial: "shared/account_sidebar" + = yield :sidebar diff --git a/app/views/shared/_signed_in.html.haml b/app/views/shared/_signed_in.html.haml index a0137015b7..f3236cfa73 100644 --- a/app/views/shared/_signed_in.html.haml +++ b/app/views/shared/_signed_in.html.haml @@ -1,5 +1,6 @@ -%li#login-link.hide= link_to "Login", "#sidebar", id: "sidebarLoginButton", class: "sidebar-button" -%li#login-name= link_to "#{spree_current_user.email}", "#" +%li#login-name + %a.sidebar-button{"ng-click" => "toggle('/account')"} + = spree_current_user.email %li.divider -%li#sign-up-link.hide= link_to "Sign Up", "#" -%li#sign-out-link= link_to "Sign Out", "/logout" + %li#sign-out-link + = link_to "Sign Out", "/logout" diff --git a/app/views/shared/_signed_out.html.haml b/app/views/shared/_signed_out.html.haml index 1705c6b5bf..b42c104769 100644 --- a/app/views/shared/_signed_out.html.haml +++ b/app/views/shared/_signed_out.html.haml @@ -1,8 +1,5 @@ %li#login-link - %a.sidebar-button{"ng-click" => "toggleLogin()"} Login - -%li#login-name.hide + %a.sidebar-button{"ng-click" => "toggle('/login')"} Login %li.divider - %li#sign-up-link - %a.sidebar-button{"ng-click" => "toggleSignup()"} Sign Up + %a.sidebar-button{"ng-click" => "toggle('/signup')"} Sign Up diff --git a/app/views/shop/_modals.html.haml b/app/views/shop/_modals.html.haml index ea21914397..ad92f1957f 100644 --- a/app/views/shop/_modals.html.haml +++ b/app/views/shop/_modals.html.haml @@ -1,5 +1,5 @@ - for producer in current_producers - .reveal-modal{id: "producer_details_#{producer.id}"} + .reveal-modal{id: "producer_details_#{producer.id}", "data-reveal" => ""} .row - if producer.logo.exists? .large-1.columns @@ -17,10 +17,9 @@ %a.close-reveal-modal × - - for group in current_distributor.groups - for sibling in group.enterprises.except(current_distributor) - .reveal-modal{id: "sibling_details_#{sibling.id}"} + .reveal-modal{id: "sibling_details_#{sibling.id}", "data-reveal" => ""} .row - if sibling.logo.exists? .large-1.columns @@ -35,4 +34,3 @@ .large-4.columns %img.about.right{src: sibling.promo_image.url(:large)} %a.close-reveal-modal × - diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 0445e28eed..8ac9f67c90 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -24,12 +24,12 @@ .large-6.columns = ba.text_field :lastname, "ng-model" => "order.bill_address.lastname" - %fieldset + %fieldset#billing %legend Billing Address = f.fields_for :bill_address, @order.bill_address do |ba| .row .large-12.columns - = ba.text_field :address1, label: "Billing Address", + = ba.text_field :address1, "ng-model" => "order.bill_address.address1" .row .large-12.columns diff --git a/app/views/shop/shop/_groups.html.haml b/app/views/shop/shop/_groups.html.haml index 6a0f6ff290..a218d6aeb5 100644 --- a/app/views/shop/shop/_groups.html.haml +++ b/app/views/shop/shop/_groups.html.haml @@ -6,5 +6,5 @@ %ul - for sibling in group.enterprises.except(current_distributor) %li - %a{"data-reveal-id" => "sibling_details_#{sibling.id}"} + %a{"data-reveal-id" => "sibling_details_#{sibling.id}", "data-reveal" => ""} = sibling.name diff --git a/app/views/shop/shop/_producers.html.haml b/app/views/shop/shop/_producers.html.haml index 1b22361a14..32f09869e8 100644 --- a/app/views/shop/shop/_producers.html.haml +++ b/app/views/shop/shop/_producers.html.haml @@ -2,5 +2,5 @@ %ul - for producer in current_producers %li - %a{"data-reveal-id" => "producer_details_#{producer.id}"} + %a{"data-reveal-id" => "producer_details_#{producer.id}", "data-reveal" => ""} = producer.name diff --git a/app/views/shop/shop/_products.html.haml b/app/views/shop/shop/_products.html.haml index 36193467e0..f8a2cbdbf7 100644 --- a/app/views/shop/shop/_products.html.haml +++ b/app/views/shop/shop/_products.html.haml @@ -12,6 +12,10 @@ %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"} %tr{"class" => "product product-{{ product.id }}"} %td.name @@ -19,7 +23,7 @@ %div %h5 {{ product.name }} - %a{"data-reveal-id" => "producer_details_{{product.supplier.id}}"} + %a{"data-reveal-id" => "producer_details_{{product.supplier.id}}", "data-reveal" => ""} {{ product.supplier.name }} %td.notes {{ product.notes | truncate:80 }} %td diff --git a/app/views/shop/shop/show.html.haml b/app/views/shop/shop/show.html.haml index df054840da..f8380c6c39 100644 --- a/app/views/shop/shop/show.html.haml +++ b/app/views/shop/shop/show.html.haml @@ -1,14 +1,16 @@ %shop.darkswarm - content_for :order_cycle_form do + %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}"} + "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}", + "popover-placement" => "bottom", "popover" => "testy", "popover-trigger" => "openTrigger"} - %closing - -#%img{src: "/icon/goes/here"} + %closing{"ng-if" => "OrderCycle.selected()"} Orders close %strong {{ OrderCycle.orders_close_at() | date_in_words }} + = render partial: "shop/details" %products.row diff --git a/app/views/spree/order_mailer/confirm_email.text.erb b/app/views/spree/order_mailer/confirm_email.text.erb index e72fe48433..12208bb8a4 100644 --- a/app/views/spree/order_mailer/confirm_email.text.erb +++ b/app/views/spree/order_mailer/confirm_email.text.erb @@ -23,6 +23,15 @@ Payment Details <%= @order.payments.first.andand.payment_method.andand.description.andand.html_safe %> <% end %> + +<%- if @order.shipping_method.andand.require_ship_address %> +============================================================ +Shipping Details +============================================================ +Your order will be shipped to: +<%= @order.ship_address.to_s %> +<% else %> + ============================================================ Collection / Delivery Details ============================================================ @@ -34,9 +43,11 @@ Collection / Delivery Details <% else %> <%= @order.distributor.next_collection_at %> <% end %> +<% end %> Thanks for your support. + <%= @order.distributor.contact %>, <%= @order.distributor.name %> <%= @order.distributor.phone %> diff --git a/app/views/spree/orders/edit.html.haml b/app/views/spree/orders/edit.html.haml index 3ac65a197b..d732c5fc59 100644 --- a/app/views/spree/orders/edit.html.haml +++ b/app/views/spree/orders/edit.html.haml @@ -41,18 +41,18 @@ \: %span.order-total.grand-total= @order.display_total - .links{'data-hook' => "cart_buttons"} - .row - #empty-cart.columns.large-9{"data-hook" => ""} - = form_tag empty_cart_path, :method => :put do - #clear_cart_link{"data-hook" => ""} - = link_to "Continue Shopping", main_app.shop_path, class: "button secondary" - = t(:or) - = submit_tag t(:empty_cart), :class => 'button secondary' + .links{'data-hook' => "cart_buttons"} + .row + #empty-cart.columns.large-8{"data-hook" => ""} + = form_tag empty_cart_path, :method => :put do + #clear_cart_link{"data-hook" => ""} + = link_to "Continue Shopping", main_app.shop_path, class: "button secondary" + = t(:or) + = submit_tag t(:empty_cart), :class => 'button secondary' - .columns.large-1 - = button_tag :class => 'secondary', :id => 'update-button' do - = t(:update) - .columns.large-2 - = link_to "Checkout", main_app.shop_checkout_path, class: "button checkout primary", id: "checkout-link" + .columns.large-4.text-right + = button_tag :class => 'secondary', :id => 'update-button' do + = t(:update) + = t(:or) + = link_to "Checkout", main_app.shop_checkout_path, class: "button checkout primary", id: "checkout-link" diff --git a/app/views/spree/user_mailer/signup_confirmation.text.erb b/app/views/spree/user_mailer/signup_confirmation.text.erb new file mode 100644 index 0000000000..c6e57efc8a --- /dev/null +++ b/app/views/spree/user_mailer/signup_confirmation.text.erb @@ -0,0 +1,13 @@ +Hello, + +Welcome to Australia's Open Food Network! Your login email is <%= @user.email %> + +You can go online and start shopping through food hubs and local producers you like at vic.openfoodnetwork.org.au + +We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at hello@openfoodnetwork.org + +Thanks for getting on board and we look forward to introducing you to many more great farmers, food hubs and food! + +Cheers, +Kirsten Larsen and the OFN Team + diff --git a/app/views/spree/users/edit.html.haml b/app/views/spree/users/edit.html.haml new file mode 100644 index 0000000000..9815b5f34d --- /dev/null +++ b/app/views/spree/users/edit.html.haml @@ -0,0 +1,8 @@ +.darkswarm + .row + = render :partial => 'spree/shared/error_messages', :locals => { :target => @user } + %h1= t(:editing_user) + = form_for Spree::User.new, :as => @user, :url => spree.user_path(@user), :method => :put do |f| + = render :partial => 'spree/shared/user_form', :locals => { :f => f } + %p + = f.submit t(:update), :class => 'button primary' diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml new file mode 100644 index 0000000000..49db903a14 --- /dev/null +++ b/app/views/spree/users/show.html.haml @@ -0,0 +1,33 @@ +.darkswarm + .row + %h1= accurate_title + .account-summary{"data-hook" => "account_summary"} + %dl#user-info + %dt= t(:email) + %dd + = @user.email + (#{link_to t(:edit), spree.edit_account_path}) + .account-my-orders{"data-hook" => "account_my_orders"} + %h3= t(:my_orders) + - if @orders.present? + %table.order-summary + %thead + %tr + %th.order-number= t(:order_number) + %th.order-date= t(:order_date) + %th.order-status= t(:status) + %th.order-payment-state= t(:payment_state) + %th.order-shipment-state= t(:shipment_state) + %th.order-total= t(:total) + %tbody + - @orders.each do |order| + %tr{class: cycle('even', 'odd')} + %td.order-number= link_to order.number, order_url(order) + %td.order-date= l order.completed_at.to_date + %td.order-status= t(order.state).titleize + %td.order-payment-state= t("payment_states.#{order.payment_state}") if order.payment_state + %td.order-shipment-state= t("shipment_states.#{order.shipment_state}") if order.shipment_state + %td.order-total= money order.total + - else + %p= t(:you_have_no_orders_yet) + %br/ diff --git a/app/views/user_passwords/edit.html.haml b/app/views/user_passwords/edit.html.haml new file mode 100644 index 0000000000..3042161881 --- /dev/null +++ b/app/views/user_passwords/edit.html.haml @@ -0,0 +1,16 @@ += f_form_for @spree_user, :as => :spree_user, :url => spree.spree_user_password_path, :method => :put do |f| + = render :partial => 'spree/shared/error_messages', :locals => { :target => @spree_user } + %fieldset + .row + .small-12.medium-6.large-4.columns.medium-centered.large-centered + %legend= t(:change_my_password) + .row + .small-12.medium-6.large-4.columns.medium-centered.large-centered + = f.password_field :password + .row + .small-12.medium-6.large-4.columns.medium-centered.large-centered + = f.password_field :password_confirmation + = f.hidden_field :reset_password_token + .row + .small-10.medium-6.large-4.columns.small-centered + = f.submit t(:update_password), :class => 'button primary' diff --git a/db/migrate/20140402032034_add_missing_indexes.rb b/db/migrate/20140402032034_add_missing_indexes.rb new file mode 100644 index 0000000000..5c7713d3b6 --- /dev/null +++ b/db/migrate/20140402032034_add_missing_indexes.rb @@ -0,0 +1,38 @@ +class AddMissingIndexes < ActiveRecord::Migration + def change + add_index :adjustment_metadata, :enterprise_id + + add_index :carts, :user_id + + add_index :coordinator_fees, :order_cycle_id + add_index :coordinator_fees, :enterprise_fee_id + + add_index :distributors_payment_methods, :distributor_id + add_index :distributors_payment_methods, :payment_method_id + + add_index :enterprise_fees, :enterprise_id + + add_index :enterprise_groups_enterprises, :enterprise_group_id + add_index :enterprise_groups_enterprises, :enterprise_id + + add_index :enterprise_roles, :user_id + add_index :enterprise_roles, :enterprise_id + + add_index :enterprises, :address_id + + add_index :exchange_fees, :exchange_id + add_index :exchange_fees, :enterprise_fee_id + + add_index :exchange_variants, :exchange_id + add_index :exchange_variants, :variant_id + + add_index :exchanges, :order_cycle_id + add_index :exchanges, :sender_id + add_index :exchanges, :receiver_id + add_index :exchanges, :payment_enterprise_id + + add_index :product_distributions, :product_id + add_index :product_distributions, :distributor_id + add_index :product_distributions, :enterprise_fee_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 892382d1d7..83688ff525 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20140324025840) do +ActiveRecord::Schema.define(:version => 20140402032034) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -22,11 +22,14 @@ ActiveRecord::Schema.define(:version => 20140324025840) do end add_index "adjustment_metadata", ["adjustment_id"], :name => "index_adjustment_metadata_on_adjustment_id" + add_index "adjustment_metadata", ["enterprise_id"], :name => "index_adjustment_metadata_on_enterprise_id" create_table "carts", :force => true do |t| t.integer "user_id" end + add_index "carts", ["user_id"], :name => "index_carts_on_user_id" + create_table "cms_blocks", :force => true do |t| t.integer "page_id", :null => false t.string "identifier", :null => false @@ -149,11 +152,17 @@ ActiveRecord::Schema.define(:version => 20140324025840) do t.integer "enterprise_fee_id" end + add_index "coordinator_fees", ["enterprise_fee_id"], :name => "index_coordinator_fees_on_enterprise_fee_id" + add_index "coordinator_fees", ["order_cycle_id"], :name => "index_coordinator_fees_on_order_cycle_id" + create_table "distributors_payment_methods", :id => false, :force => true do |t| t.integer "distributor_id" t.integer "payment_method_id" end + add_index "distributors_payment_methods", ["distributor_id"], :name => "index_distributors_payment_methods_on_distributor_id" + add_index "distributors_payment_methods", ["payment_method_id"], :name => "index_distributors_payment_methods_on_payment_method_id" + create_table "distributors_shipping_methods", :id => false, :force => true do |t| t.integer "distributor_id" t.integer "shipping_method_id" @@ -170,6 +179,8 @@ ActiveRecord::Schema.define(:version => 20140324025840) do t.datetime "updated_at", :null => false end + 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" @@ -181,11 +192,17 @@ ActiveRecord::Schema.define(:version => 20140324025840) do t.integer "enterprise_id" end + add_index "enterprise_groups_enterprises", ["enterprise_group_id"], :name => "index_enterprise_groups_enterprises_on_enterprise_group_id" + add_index "enterprise_groups_enterprises", ["enterprise_id"], :name => "index_enterprise_groups_enterprises_on_enterprise_id" + create_table "enterprise_roles", :force => true do |t| t.integer "user_id" t.integer "enterprise_id" end + add_index "enterprise_roles", ["enterprise_id"], :name => "index_enterprise_roles_on_enterprise_id" + add_index "enterprise_roles", ["user_id"], :name => "index_enterprise_roles_on_user_id" + create_table "enterprises", :force => true do |t| t.string "name" t.string "description" @@ -215,6 +232,8 @@ ActiveRecord::Schema.define(:version => 20140324025840) do t.datetime "promo_image_updated_at" end + add_index "enterprises", ["address_id"], :name => "index_enterprises_on_address_id" + create_table "exchange_fees", :force => true do |t| t.integer "exchange_id" t.integer "enterprise_fee_id" @@ -222,6 +241,9 @@ ActiveRecord::Schema.define(:version => 20140324025840) do t.datetime "updated_at", :null => false end + add_index "exchange_fees", ["enterprise_fee_id"], :name => "index_exchange_fees_on_enterprise_fee_id" + add_index "exchange_fees", ["exchange_id"], :name => "index_exchange_fees_on_exchange_id" + create_table "exchange_variants", :force => true do |t| t.integer "exchange_id" t.integer "variant_id" @@ -229,6 +251,9 @@ ActiveRecord::Schema.define(:version => 20140324025840) do t.datetime "updated_at", :null => false end + add_index "exchange_variants", ["exchange_id"], :name => "index_exchange_variants_on_exchange_id" + add_index "exchange_variants", ["variant_id"], :name => "index_exchange_variants_on_variant_id" + create_table "exchanges", :force => true do |t| t.integer "order_cycle_id" t.integer "sender_id" @@ -241,6 +266,11 @@ ActiveRecord::Schema.define(:version => 20140324025840) do t.boolean "incoming", :default => false, :null => false end + add_index "exchanges", ["order_cycle_id"], :name => "index_exchanges_on_order_cycle_id" + add_index "exchanges", ["payment_enterprise_id"], :name => "index_exchanges_on_payment_enterprise_id" + add_index "exchanges", ["receiver_id"], :name => "index_exchanges_on_receiver_id" + add_index "exchanges", ["sender_id"], :name => "index_exchanges_on_sender_id" + create_table "landing_page_images", :force => true do |t| t.datetime "created_at", :null => false t.datetime "updated_at", :null => false @@ -267,6 +297,10 @@ ActiveRecord::Schema.define(:version => 20140324025840) do t.integer "enterprise_fee_id" end + add_index "product_distributions", ["distributor_id"], :name => "index_product_distributions_on_distributor_id" + add_index "product_distributions", ["enterprise_fee_id"], :name => "index_product_distributions_on_enterprise_fee_id" + add_index "product_distributions", ["product_id"], :name => "index_product_distributions_on_product_id" + create_table "spree_activators", :force => true do |t| t.string "description" t.datetime "expires_at" diff --git a/spec/controllers/user_passwords_controller_spec.rb b/spec/controllers/user_passwords_controller_spec.rb index e79a991b39..25c285e09b 100644 --- a/spec/controllers/user_passwords_controller_spec.rb +++ b/spec/controllers/user_passwords_controller_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' require 'spree/api/testing_support/helpers' describe UserPasswordsController do + let(:user) { create(:user) } before do @request.env["devise.mapping"] = Devise.mappings[:spree_user] @@ -15,11 +16,16 @@ describe UserPasswordsController do end it "redirects to login when data is valid" do - user = create(:user) spree_post :create, spree_user: { email: user.email} response.should be_redirect end + it "renders Darkswarm" do + user.send_reset_password_instructions + spree_get :edit, reset_password_token: user.reset_password_token + response.should render_template "user_passwords/edit" + end + describe "via ajax" do it "returns errors" do xhr :post, :create, spree_user: {}, use_route: :spree diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 20e728a09d..25eaeb69dd 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -141,9 +141,9 @@ feature %q{ page.should have_selector 'td.distributors', text: 'My distributor' # And it should have some fees - OrderCycle.last.exchanges.first.enterprise_fees.should == [supplier_fee] - OrderCycle.last.coordinator_fees.should == [coordinator_fee] - OrderCycle.last.exchanges.last.enterprise_fees.should == [distributor_fee] + OrderCycle.last.exchanges.incoming.first.enterprise_fees.should == [supplier_fee] + OrderCycle.last.coordinator_fees.should == [coordinator_fee] + OrderCycle.last.exchanges.outgoing.first.enterprise_fees.should == [distributor_fee] # And it should have some variants selected OrderCycle.last.exchanges.first.variants.count.should == 2 diff --git a/spec/features/chili/enterprises_distributor_info_rich_text_feature_spec.rb b/spec/features/chili/enterprises_distributor_info_rich_text_feature_spec.rb index 46385cee0f..10214d69dc 100644 --- a/spec/features/chili/enterprises_distributor_info_rich_text_feature_spec.rb +++ b/spec/features/chili/enterprises_distributor_info_rich_text_feature_spec.rb @@ -58,7 +58,6 @@ feature "enterprises distributor info as rich text" do end scenario "viewing distributor info with product distribution", js: true do - ActionMailer::Base.deliveries.clear d = create(:distributor_enterprise, distributor_info: 'Chu ge sai yubi dan bisento tobi ashi yubi ge omote.', next_collection_at: 'Thursday 2nd May') p = create(:product, :distributors => [d]) @@ -67,6 +66,7 @@ feature "enterprises distributor info as rich text" do login_to_consumer_section visit spree.select_distributor_order_path(d) + ActionMailer::Base.deliveries.clear # -- Product details page visit spree.product_path p @@ -110,6 +110,7 @@ feature "enterprises distributor info as rich text" do setup_shipping_details d login_to_consumer_section + ActionMailer::Base.deliveries.clear click_link 'Green Grass' visit enterprise_path d @@ -146,7 +147,7 @@ feature "enterprises distributor info as rich text" do zone = create(:zone) c = Spree::Country.find_by_name('Australia') Spree::ZoneMember.create(:zoneable => c, :zone => zone) - create(:shipping_method, zone: zone) + create(:shipping_method, zone: zone, require_ship_address: false) create(:payment_method, :description => 'Cheque payment method', distributors: [distributor]) end diff --git a/spec/features/consumer/shopping/checkout_auth_spec.rb b/spec/features/consumer/shopping/checkout_auth_spec.rb index de14f121e7..6ad1ab89d4 100644 --- a/spec/features/consumer/shopping/checkout_auth_spec.rb +++ b/spec/features/consumer/shopping/checkout_auth_spec.rb @@ -3,16 +3,16 @@ require 'spec_helper' feature "As a consumer I want to check out my cart", js: true do include AuthenticationWorkflow include WebHelper + include ShopWorkflow let(:distributor) { create(:distributor_enterprise) } let(:supplier) { create(:supplier_enterprise) } let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } let(:product) { create(:simple_product, supplier: supplier) } + let(:order) { Spree::Order.last } before do create_enterprise_group_for distributor - exchange = Exchange.find(order_cycle.exchanges.to_enterprises(distributor).outgoing.first.id) - exchange.variants << product.master end describe "Login behaviour" do diff --git a/spec/features/consumer/shopping/checkout_plumbing_spec.rb b/spec/features/consumer/shopping/checkout_plumbing_spec.rb index b972667ca9..2295b07146 100644 --- a/spec/features/consumer/shopping/checkout_plumbing_spec.rb +++ b/spec/features/consumer/shopping/checkout_plumbing_spec.rb @@ -3,17 +3,17 @@ require 'spec_helper' feature "As a consumer I want to check out my cart", js: true do include AuthenticationWorkflow + include ShopWorkflow include WebHelper let(:distributor) { create(:distributor_enterprise) } let(:supplier) { create(:supplier_enterprise) } let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } let(:product) { create(:simple_product, supplier: supplier) } + let(:order) { Spree::Order.last } before do create_enterprise_group_for distributor - exchange = Exchange.find(order_cycle.exchanges.to_enterprises(distributor).outgoing.first.id) - exchange.variants << product.master end describe "Attempting to access checkout without meeting the preconditions" do it "redirects to the homepage if no distributor is selected" do diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 211023b5a6..7cf28675e1 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -3,19 +3,20 @@ require 'spec_helper' feature "As a consumer I want to check out my cart", js: true do include AuthenticationWorkflow + include ShopWorkflow include WebHelper let(:distributor) { create(:distributor_enterprise) } let(:supplier) { create(:supplier_enterprise) } let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } let(:product) { create(:simple_product, supplier: supplier) } + let(:order) { Spree::Order.last } before do create_enterprise_group_for distributor - exchange = Exchange.find(order_cycle.exchanges.to_enterprises(distributor).outgoing.first.id) - exchange.variants << product.master end + # Disabled :in for performance reasons [:out].each do |auth_state| describe "logged #{auth_state.to_s}, distributor selected, order cycle selected, product in cart" do let(:user) { create_enterprise_user } @@ -36,12 +37,9 @@ feature "As a consumer I want to check out my cart", js: true do distributor.shipping_methods << sm2 visit "/shop/checkout" end - it "shows all shipping methods" do + it "shows all shipping methods, but doesn't show ship address when not needed" do page.should have_content "Frogs" page.should have_content "Donkeys" - end - - it "doesn't show ship address forms when a shipping method wants no address" do choose(sm2.name) find("#ship_address", visible: false).visible?.should be_false end @@ -74,7 +72,9 @@ feature "As a consumer I want to check out my cart", js: true do it "copies billing address to hidden shipping address fields" do choose(sm1.name) check "Shipping address same as billing address?" - fill_in "Billing Address", with: "testy" + within "#billing" do + fill_in "Address", with: "testy" + end within "#ship_address_hidden" do find("#order_ship_address_attributes_address1", visible: false).value.should == "testy" end @@ -112,7 +112,7 @@ feature "As a consumer I want to check out my cart", js: true do within "#details" do fill_in "First Name", with: "Will" fill_in "Last Name", with: "Marshall" - fill_in "Billing Address", with: "123 Your Face" + fill_in "Address", with: "123 Your Face" select "Australia", from: "Country" select "Victoria", from: "State" fill_in "Customer E-Mail", with: "test@test.com" @@ -130,7 +130,7 @@ feature "As a consumer I want to check out my cart", js: true do within "#details" do fill_in "First Name", with: "Will" fill_in "Last Name", with: "Marshall" - fill_in "Billing Address", with: "123 Your Face" + fill_in "Address", with: "123 Your Face" select "Australia", from: "Country" select "Victoria", from: "State" fill_in "Customer E-Mail", with: "test@test.com" @@ -149,19 +149,3 @@ feature "As a consumer I want to check out my cart", js: true do end end - -def select_distributor - visit "/" - click_link distributor.name -end - -def select_order_cycle - exchange = Exchange.find(order_cycle.exchanges.to_enterprises(distributor).outgoing.first.id) - visit "/shop" - select exchange.pickup_time, from: "order_cycle_id" -end - -def add_product_to_cart - fill_in "variants[#{product.master.id}]", with: product.master.on_hand - 1 - first("form.custom > input.button.right").click -end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 1b3f7b613c..cb6816f69f 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -13,13 +13,10 @@ feature "As a consumer I want to shop with a distributor", js: true do click_link distributor.name end - it "shows a distributor" do + it "shows a distributor with images" do visit shop_path page.should have_text distributor.name - end - it "shows distributor images" do - visit shop_path find("#tab_about a").click first("distributor img")['src'].should == distributor.logo.url(:thumb) first("#about img")['src'].should == distributor.promo_image.url(:large) @@ -43,32 +40,27 @@ feature "As a consumer I want to shop with a distributor", js: true do end describe "selecting an order cycle" do + let(:oc1) {create(:simple_order_cycle, distributors: [distributor], orders_close_at: 2.days.from_now)} + let(:oc2) {create(:simple_order_cycle, distributors: [distributor], orders_close_at: 3.days.from_now)} + let(:exchange1) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } + let(:exchange2) { Exchange.find(oc2.exchanges.to_enterprises(distributor).outgoing.first.id) } 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" + exchange1.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], orders_close_at: 2.days.from_now)} - let(:oc2) {create(:simple_order_cycle, distributors: [distributor], orders_close_at: 3.days.from_now)} 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" + exchange1.update_attribute :pickup_time, "frogs" + exchange2.update_attribute :pickup_time, "turtles" visit shop_path end - it "shows a select with all order cycles" do + it "shows a select with all order cycles, but doesn't show the products by default" do page.should have_selector "option", text: 'frogs' page.should have_selector "option", text: 'turtles' - end - - it "doesn't show the table before an order cycle is selected" do page.should_not have_selector("input.button.right", visible: true) end @@ -80,33 +72,20 @@ feature "As a consumer I want to shop with a distributor", js: true do 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 + exchange1.variants << product.master visit shop_path end - it "allows us to select an order cycle" do - select "frogs", :from => "order_cycle_id" + it "allows us to select an order cycle, thus showing products" do + page.should_not have_content product.name Spree::Order.last.order_cycle.should == nil + + select "frogs", :from => "order_cycle_id" page.should have_selector "products" page.should have_content "Orders close 2 days from now" 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 - oc1.stub(:orders_close_at).and_return 3.days.from_now - select "turtles", :from => "order_cycle_id" - page.should have_content "Orders close 3 days from now" - end end end @@ -164,13 +143,14 @@ feature "As a consumer I want to shop with a distributor", js: true do end end - describe "filtering on hand and on demand products" do + describe "filtering products" do let(:oc) { create(:simple_order_cycle, distributors: [distributor]) } 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(: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) } @@ -183,6 +163,8 @@ feature "As a consumer I want to shop with a distributor", js: true do 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 v1.update_attribute(:count_on_hand, 1) v2.update_attribute(:count_on_hand, 0) v3.update_attribute(:count_on_hand, 0) @@ -193,6 +175,7 @@ feature "As a consumer I want to shop with a distributor", js: true do 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 @@ -224,6 +207,9 @@ feature "As a consumer I want to shop with a distributor", js: true do # 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 end end diff --git a/spec/javascripts/unit/darkswarm/navigation.js.coffee b/spec/javascripts/unit/darkswarm/navigation.js.coffee new file mode 100644 index 0000000000..6630f8f955 --- /dev/null +++ b/spec/javascripts/unit/darkswarm/navigation.js.coffee @@ -0,0 +1,16 @@ +describe 'Navigation service', -> + Navigation = null + + beforeEach -> + module 'Darkswarm' + inject ($injector)-> + Navigation = $injector.get("Navigation") + + it "caches the path provided", -> + Navigation.navigate "/foo" + expect(Navigation.path).toEqual "/foo" + + it "defaults to the first path in the list", -> + Navigation.paths = ["/test", "/bar"] + Navigation.navigate() + expect(Navigation.path).toEqual "/test" diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 0466a5b861..d6d0ac882b 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -19,6 +19,7 @@ describe 'OrderCycle controllers', -> variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').andReturn('variant supplied') exchangeDirection: jasmine.createSpy('exchangeDirection').andReturn('exchange direction') toggleProducts: jasmine.createSpy('toggleProducts') + setExchangeVariants: jasmine.createSpy('setExchangeVariants') addSupplier: jasmine.createSpy('addSupplier') addDistributor: jasmine.createSpy('addDistributor') removeExchange: jasmine.createSpy('removeExchange') @@ -31,6 +32,7 @@ describe 'OrderCycle controllers', -> Enterprise = index: jasmine.createSpy('index').andReturn('enterprises list') supplied_products: 'supplied products' + suppliedVariants: jasmine.createSpy('suppliedVariants').andReturn('supplied variants') totalVariants: jasmine.createSpy('totalVariants').andReturn('variants total') EnterpriseFee = index: jasmine.createSpy('index').andReturn('enterprise fees list') @@ -63,10 +65,18 @@ describe 'OrderCycle controllers', -> EnterpriseFee.loaded = false expect(scope.loaded()).toBe(false) + it "delegates suppliedVariants to Enterprise", -> + expect(scope.suppliedVariants('enterprise_id')).toEqual('supplied variants') + expect(Enterprise.suppliedVariants).toHaveBeenCalledWith('enterprise_id') + it 'Delegates exchangeSelectedVariants to OrderCycle', -> expect(scope.exchangeSelectedVariants('exchange')).toEqual('variants selected') expect(OrderCycle.exchangeSelectedVariants).toHaveBeenCalledWith('exchange') + it "delegates setExchangeVariants to OrderCycle", -> + scope.setExchangeVariants('exchange', 'variants', 'selected') + expect(OrderCycle.setExchangeVariants).toHaveBeenCalledWith('exchange', 'variants', 'selected') + it 'Delegates enterpriseTotalVariants to Enterprise', -> expect(scope.enterpriseTotalVariants('enterprise')).toEqual('variants total') expect(Enterprise.totalVariants).toHaveBeenCalledWith('enterprise') @@ -169,6 +179,7 @@ describe 'OrderCycle controllers', -> variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').andReturn('variant supplied') exchangeDirection: jasmine.createSpy('exchangeDirection').andReturn('exchange direction') toggleProducts: jasmine.createSpy('toggleProducts') + setExchangeVariants: jasmine.createSpy('setExchangeVariants') addSupplier: jasmine.createSpy('addSupplier') addDistributor: jasmine.createSpy('addDistributor') removeExchange: jasmine.createSpy('removeExchange') @@ -181,6 +192,7 @@ describe 'OrderCycle controllers', -> Enterprise = index: jasmine.createSpy('index').andReturn('enterprises list') supplied_products: 'supplied products' + suppliedVariants: jasmine.createSpy('suppliedVariants').andReturn('supplied variants') totalVariants: jasmine.createSpy('totalVariants').andReturn('variants total') EnterpriseFee = index: jasmine.createSpy('index').andReturn('enterprise fees list') @@ -213,10 +225,18 @@ describe 'OrderCycle controllers', -> OrderCycle.loaded = false expect(scope.loaded()).toBe(false) + it "delegates suppliedVariants to Enterprise", -> + expect(scope.suppliedVariants('enterprise_id')).toEqual('supplied variants') + expect(Enterprise.suppliedVariants).toHaveBeenCalledWith('enterprise_id') + it 'Delegates exchangeSelectedVariants to OrderCycle', -> expect(scope.exchangeSelectedVariants('exchange')).toEqual('variants selected') expect(OrderCycle.exchangeSelectedVariants).toHaveBeenCalledWith('exchange') + it "delegates setExchangeVariants to OrderCycle", -> + scope.setExchangeVariants('exchange', 'variants', 'selected') + expect(OrderCycle.setExchangeVariants).toHaveBeenCalledWith('exchange', 'variants', 'selected') + it 'Delegates totalVariants to Enterprise', -> expect(scope.enterpriseTotalVariants('enterprise')).toEqual('variants total') expect(Enterprise.totalVariants).toHaveBeenCalledWith('enterprise') @@ -332,6 +352,25 @@ describe 'OrderCycle services', -> $httpBackend.flush() expect(Enterprise.supplied_products).toEqual [1, 2, 3, 4, 5, 6] + it "finds supplied variants for an enterprise", -> + spyOn(Enterprise, 'variantsOf').andReturn(10) + Enterprise.index() + $httpBackend.flush() + expect(Enterprise.suppliedVariants(1)).toEqual [10, 10] + + describe "finding the variants of a product", -> + it "returns the master for products without variants", -> + p = + master_id: 1 + variants: [] + expect(Enterprise.variantsOf(p)).toEqual [1] + + it "returns the variant ids for products with variants", -> + p = + master_id: 1 + variants: [{id: 2}, {id: 3}] + expect(Enterprise.variantsOf(p)).toEqual [2, 3] + it 'counts total variants supplied by an enterprise', -> enterprise = supplied_products: [ @@ -450,6 +489,12 @@ describe 'OrderCycle services', -> OrderCycle.toggleProducts(exchange) expect(exchange.showProducts).toEqual(true) + describe "setting exchange variants", -> + it "sets all variants to the provided value", -> + exchange = {variants: {2: false}} + OrderCycle.setExchangeVariants(exchange, [1, 2, 3], true) + expect(exchange.variants).toEqual {1: true, 2: true, 3: true} + describe 'adding suppliers', -> exchange = null @@ -725,35 +770,62 @@ describe 'OrderCycle services', -> ] it 'converts coordinator fees into a list of ids', -> - data = + order_cycle = coordinator_fees: [ {id: 1} {id: 2} ] - data = OrderCycle.translateCoordinatorFees(data) + data = OrderCycle.translateCoordinatorFees(order_cycle) expect(data.coordinator_fees).toBeUndefined() expect(data.coordinator_fee_ids).toEqual [1, 2] - it 'converts exchange fees into a list of ids', -> - data = - incoming_exchanges: [ - enterprise_fees: [ - {id: 1} - {id: 2} + it "preserves original data when converting coordinator fees", -> + OrderCycle.order_cycle = + coordinator_fees: [ + {id: 1} + {id: 2} ] - ] - outgoing_exchanges: [ - enterprise_fees: [ - {id: 3} - {id: 4} + + data = OrderCycle.deepCopy() + data = OrderCycle.translateCoordinatorFees(data) + + expect(OrderCycle.order_cycle.coordinator_fees).toEqual [{id: 1}, {id: 2}] + expect(OrderCycle.order_cycle.coordinator_fee_ids).toBeUndefined() + + describe "converting exchange fees into a list of ids", -> + order_cycle = null + data = null + + beforeEach -> + order_cycle = + incoming_exchanges: [ + enterprise_fees: [ + {id: 1} + {id: 2} + ] ] - ] + outgoing_exchanges: [ + enterprise_fees: [ + {id: 3} + {id: 4} + ] + ] + OrderCycle.order_cycle = order_cycle - data = OrderCycle.translateExchangeFees(data) + data = OrderCycle.deepCopy() + data = OrderCycle.translateExchangeFees(data) - 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] + it 'converts exchange fees into a list of ids', -> + 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] + + it "preserves original data when converting exchange fees", -> + expect(order_cycle.incoming_exchanges[0].enterprise_fees).toEqual [{id: 1}, {id: 2}] + expect(order_cycle.outgoing_exchanges[0].enterprise_fees).toEqual [{id: 3}, {id: 4}] + expect(order_cycle.incoming_exchanges[0].enterprise_fee_ids).toBeUndefined() + expect(order_cycle.outgoing_exchanges[0].enterprise_fee_ids).toBeUndefined() + \ No newline at end of file diff --git a/spec/mailers/order_mailer_spec.rb b/spec/mailers/order_mailer_spec.rb index f0535d88a7..0fae742ef9 100644 --- a/spec/mailers/order_mailer_spec.rb +++ b/spec/mailers/order_mailer_spec.rb @@ -17,6 +17,7 @@ describe Spree::OrderMailer do product_distribution = create(:product_distribution, :product => product, :distributor => @distributor) @shipping_instructions = "pick up on thursday please!" @order1 = create(:order, :distributor => @distributor, :bill_address => @bill_address, :special_instructions => @shipping_instructions) + ActionMailer::Base.deliveries = [] end it "should send an email when given an order" do diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb new file mode 100644 index 0000000000..97e0eb4430 --- /dev/null +++ b/spec/mailers/user_mailer_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Spree::UserMailer do + let(:user) { build(:user) } + + after do + ActionMailer::Base.deliveries.clear + end + + before do + ActionMailer::Base.delivery_method = :test + ActionMailer::Base.perform_deliveries = true + ActionMailer::Base.deliveries = [] + end + + it "sends an email when given a user" do + Spree::UserMailer.signup_confirmation(user).deliver + ActionMailer::Base.deliveries.count.should == 1 + end +end diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index 403fceba87..9614a4d357 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -13,13 +13,17 @@ describe Exchange do end end - it "should not be valid when sender and receiver pair are not unique for its order cycle" do + it "should not be valid when (sender, receiver, direction) set are not unique for its order cycle" do e1 = create(:exchange) e2 = build(:exchange, - :order_cycle => e1.order_cycle, :sender => e1.sender, :receiver => e1.receiver) + :order_cycle => e1.order_cycle, :sender => e1.sender, :receiver => e1.receiver, :incoming => e1.incoming) e2.should_not be_valid + e2.incoming = !e2.incoming + e2.should be_valid + e2.incoming = !e2.incoming + e2.receiver = create(:enterprise) e2.should be_valid diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 18ad347dc2..bbdb6e22b9 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -181,14 +181,19 @@ describe Spree::Order do end describe "setting the order cycle" do + let(:oc) { create(:simple_order_cycle) } + + it "empties the cart when changing the order cycle" do + subject.should_receive(:empty!) + subject.set_order_cycle! oc + end + it "sets the order cycle when no distributor is set" do - oc = create(:simple_order_cycle) subject.set_order_cycle! oc subject.order_cycle.should == oc end it "keeps the distributor when it is available in the new order cycle" do - oc = create(:simple_order_cycle) d = create(:distributor_enterprise) create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d, incoming: false) @@ -200,7 +205,6 @@ describe Spree::Order do end it "clears the distributor if it is not available at that order cycle" do - oc = create(:simple_order_cycle) d = create(:distributor_enterprise) subject.distributor = d @@ -211,7 +215,6 @@ describe Spree::Order do end it "clears the order cycle when setting to nil" do - oc = create(:simple_order_cycle) d = create(:distributor_enterprise) subject.set_order_cycle! oc subject.distributor = d diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb new file mode 100644 index 0000000000..4e9ad94fac --- /dev/null +++ b/spec/models/spree/user_spec.rb @@ -0,0 +1,9 @@ +describe Spree.user_class do + context "#create" do + + it "should send a signup email" do + Spree::UserMailer.should_receive(:signup_confirmation).and_return(double(:deliver => true)) + create(:user) + end + end +end diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index d51a34e254..ecc0423e85 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -115,6 +115,41 @@ module Spree end describe "unit value/description" do + describe "setting the variant's weight from the unit value" do + it "sets the variant's weight when unit is weight" do + p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil) + v = create(:variant, product: p, weight: nil) + + p.update_attributes! variant_unit: 'weight', variant_unit_scale: 1 + v.update_attributes! unit_value: 10, unit_description: 'foo' + + v.reload.weight.should == 0.01 + end + + it "does nothing when unit is not weight" do + p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil) + v = create(:variant, product: p, weight: 123) + + p.update_attributes! variant_unit: 'volume', variant_unit_scale: 1 + v.update_attributes! unit_value: 10, unit_description: 'foo' + + v.reload.weight.should == 123 + end + + it "does nothing when unit_value is not set" do + p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil) + v = create(:variant, product: p, weight: 123) + + p.update_attributes! variant_unit: 'weight', variant_unit_scale: 1 + + # Although invalid, this calls the before_validation callback, which would + # error if not handling unit_value == nil case + v.update_attributes(unit_value: nil, unit_description: 'foo').should be_false + + v.reload.weight.should == 123 + end + end + context "when the variant initially has no value" do context "when the required option value does not exist" do let!(:p) { create(:simple_product, variant_unit: nil, variant_unit_scale: nil) } diff --git a/spec/support/request/shop_workflow.rb b/spec/support/request/shop_workflow.rb new file mode 100644 index 0000000000..414c7a4fa1 --- /dev/null +++ b/spec/support/request/shop_workflow.rb @@ -0,0 +1,19 @@ +module ShopWorkflow + def select_distributor + # If no order cycles are available this is much faster + visit "/" + click_link distributor.name + end + + # These methods are naughty and write to the DB directly + # Because loading the whole Angular app is slow + def select_order_cycle + exchange = Exchange.find(order_cycle.exchanges.to_enterprises(distributor).outgoing.first.id) + exchange.variants << product.master + order.update_attribute :order_cycle, order_cycle + end + + def add_product_to_cart + create(:line_item, variant: product.master, order: order) + end +end