diff --git a/.gitignore b/.gitignore index b587584d3b..f60933a3c1 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ config/initializers/feature_toggle.rb NERD_tree* coverage libpeerconnection.log +tags +app/assets/javascripts/tags diff --git a/app/assets/images/map_001-producer-only.svg b/app/assets/images/map_001-producer-only.svg index b34c01f1ed..423442a68f 100644 --- a/app/assets/images/map_001-producer-only.svg +++ b/app/assets/images/map_001-producer-only.svg @@ -11,53 +11,57 @@ ]> + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="33px" + viewBox="0 0 28 33" enable-background="new 0 0 28 33" xml:space="preserve"> - + - + - - - - - - + + + + + + + + + diff --git a/app/assets/images/map_002-producer-only-profile.svg b/app/assets/images/map_002-producer-only-profile.svg index 0522f91257..f84834f6c6 100644 --- a/app/assets/images/map_002-producer-only-profile.svg +++ b/app/assets/images/map_002-producer-only-profile.svg @@ -11,55 +11,59 @@ ]> + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="33px" + viewBox="0 0 28 33" enable-background="new 0 0 28 33" xml:space="preserve"> - + - + - - - - - + + + + + + + + diff --git a/app/assets/images/map_003-producer-shop.svg b/app/assets/images/map_003-producer-shop.svg index 84c324ac4e..82ca2ce55c 100644 --- a/app/assets/images/map_003-producer-shop.svg +++ b/app/assets/images/map_003-producer-shop.svg @@ -11,53 +11,57 @@ ]> + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="33px" + viewBox="0 0 28 33" enable-background="new 0 0 28 33" xml:space="preserve"> - + - + - - - - - - + + + + + + + + + diff --git a/app/assets/images/map_004-producer-shop-profile.svg b/app/assets/images/map_004-producer-shop-profile.svg index 4d35236813..06316e61a3 100644 --- a/app/assets/images/map_004-producer-shop-profile.svg +++ b/app/assets/images/map_004-producer-shop-profile.svg @@ -11,55 +11,59 @@ ]> + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="33px" + viewBox="0 0 28 33" enable-background="new 0 0 28 33" xml:space="preserve"> - + - + - - - - - + + + + + + + + diff --git a/app/assets/images/map_005-hub.svg b/app/assets/images/map_005-hub.svg index be2d292c20..13c0b99779 100644 --- a/app/assets/images/map_005-hub.svg +++ b/app/assets/images/map_005-hub.svg @@ -11,36 +11,41 @@ ]> + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="33px" + viewBox="0 0 28 33" enable-background="new 0 0 28 33" xml:space="preserve"> - + - + - - - - + + + + + + + diff --git a/app/assets/images/map_006-hub-profile.svg b/app/assets/images/map_006-hub-profile.svg index b284ea34f3..57347a38e3 100644 --- a/app/assets/images/map_006-hub-profile.svg +++ b/app/assets/images/map_006-hub-profile.svg @@ -11,38 +11,41 @@ ]> + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="33px" + viewBox="0 0 28 33" enable-background="new 0 0 28 33" xml:space="preserve"> - + - + - - - + + + + + + diff --git a/app/assets/images/map_007-shop.svg b/app/assets/images/map_007-shop.svg index e2ebe118ce..5007c75e5f 100644 --- a/app/assets/images/map_007-shop.svg +++ b/app/assets/images/map_007-shop.svg @@ -11,37 +11,42 @@ ]> + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="33px" + viewBox="0 0 28 33" enable-background="new 0 0 28 33" xml:space="preserve"> - + - + - - - + + + + + + diff --git a/app/assets/images/map_008-shop-profile.svg b/app/assets/images/map_008-shop-profile.svg index 635602b7bf..cab4e2e01f 100644 --- a/app/assets/images/map_008-shop-profile.svg +++ b/app/assets/images/map_008-shop-profile.svg @@ -11,56 +11,61 @@ ]> + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="33px" + viewBox="0 0 28 33" enable-background="new 0 0 28 33" xml:space="preserve"> - + - - + - - - - - - + + - + - + - - - - + + + + + + + + + + + + diff --git a/app/assets/images/potatoes.png b/app/assets/images/potatoes.png new file mode 100644 index 0000000000..c0be04200e Binary files /dev/null and b/app/assets/images/potatoes.png differ diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 5ffb99ccb8..3fb5e69499 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -22,5 +22,6 @@ //= require ./payment_methods/payment_methods //= require ./products/products //= require ./shipping_methods/shipping_methods +//= require ./users/users //= require_tree . diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index d545245eeb..4c1a319c1a 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -1,6 +1,7 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ - "$scope", "$http", "dataFetcher", "blankOption", "pendingChanges", "VariantUnitManager", "OptionValueNamer", - ($scope, $http, dataFetcher, blankOption, pendingChanges, VariantUnitManager, OptionValueNamer) -> + "$scope", "$http", "dataFetcher", "blankOption", "pendingChanges", "VariantUnitManager", "OptionValueNamer", "SpreeApiKey" + ($scope, $http, dataFetcher, blankOption, pendingChanges, VariantUnitManager, OptionValueNamer, SpreeApiKey) -> + $scope.loading = true $scope.initialiseVariables = -> start = daysFromToday -7 @@ -32,14 +33,14 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ quantity: { name: "Quantity", visible: true } max: { name: "Max", visible: true } - $scope.initialise = (spree_api_key) -> + $scope.initialise = -> $scope.initialiseVariables() authorise_api_reponse = "" - dataFetcher("/api/users/authorise_api?token=" + spree_api_key).then (data) -> + dataFetcher("/api/users/authorise_api?token=" + SpreeApiKey).then (data) -> authorise_api_reponse = data $scope.spree_api_key_ok = data.hasOwnProperty("success") and data["success"] == "Use of API Authorised" if $scope.spree_api_key_ok - $http.defaults.headers.common["X-Spree-Token"] = spree_api_key + $http.defaults.headers.common["X-Spree-Token"] = SpreeApiKey dataFetcher("/api/enterprises/accessible?template=bulk_index&q[is_primary_producer_eq]=true").then (data) -> $scope.suppliers = data $scope.suppliers.unshift blankOption() diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 35ec6e694c..b68e3340e1 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -1,6 +1,8 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", [ - "$scope", "$timeout", "$http", "dataFetcher", "DirtyProducts", "VariantUnitManager", "producers", "Taxons", - ($scope, $timeout, $http, dataFetcher, DirtyProducts, VariantUnitManager, producers, Taxons) -> + "$scope", "$timeout", "$http", "dataFetcher", "DirtyProducts", "VariantUnitManager", "producers", "Taxons", "SpreeApiKey", + ($scope, $timeout, $http, dataFetcher, DirtyProducts, VariantUnitManager, producers, Taxons, SpreeApiKey) -> + $scope.loading = true + $scope.updateStatusMessage = text: "" style: {} @@ -42,14 +44,13 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", [ $scope.limit = 15 $scope.productsWithUnsavedVariants = [] - - $scope.initialise = (spree_api_key) -> + $scope.initialise = -> authorise_api_reponse = "" - dataFetcher("/api/users/authorise_api?token=" + spree_api_key).then (data) -> + dataFetcher("/api/users/authorise_api?token=" + SpreeApiKey).then (data) -> authorise_api_reponse = data $scope.spree_api_key_ok = data.hasOwnProperty("success") and data["success"] == "Use of API Authorised" if $scope.spree_api_key_ok - $http.defaults.headers.common["X-Spree-Token"] = spree_api_key + $http.defaults.headers.common["X-Spree-Token"] = SpreeApiKey $scope.fetchProducts() else if authorise_api_reponse.hasOwnProperty("error") $scope.api_error_msg = authorise_api_reponse("error") diff --git a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee index cdf90cfb51..6189661035 100644 --- a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee +++ b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee @@ -1 +1 @@ -angular.module("admin.enterprises", ["admin.payment_methods", "admin.shipping_methods"]) \ No newline at end of file +angular.module("admin.enterprises", ["admin.payment_methods", "admin.shipping_methods", "admin.users"]) \ No newline at end of file diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index b8068681ca..64f2550466 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -197,12 +197,13 @@ angular.module('order_cycle', ['ngResource']) this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: []}) removeExchange: (exchange) -> - incoming_index = this.order_cycle.incoming_exchanges.indexOf exchange - this.order_cycle.incoming_exchanges.splice(incoming_index, 1) if incoming_index > -1 - outgoing_index = this.order_cycle.outgoing_exchanges.indexOf exchange - this.order_cycle.outgoing_exchanges.splice(outgoing_index, 1) if outgoing_index > -1 - - this.removeDistributionOfVariant(variant_id) for variant_id, active of exchange.variants when active + if exchange.incoming + incoming_index = this.order_cycle.incoming_exchanges.indexOf exchange + this.order_cycle.incoming_exchanges.splice(incoming_index, 1) + this.removeDistributionOfVariant(variant_id) for variant_id, active of exchange.variants when active + else + outgoing_index = this.order_cycle.outgoing_exchanges.indexOf exchange + this.order_cycle.outgoing_exchanges.splice(outgoing_index, 1) if outgoing_index > -1 addCoordinatorFee: -> this.order_cycle.coordinator_fees.push({}) diff --git a/app/assets/javascripts/admin/users/directives/user_autocomplete.js.coffee b/app/assets/javascripts/admin/users/directives/user_autocomplete.js.coffee new file mode 100644 index 0000000000..a904d1bcce --- /dev/null +++ b/app/assets/javascripts/admin/users/directives/user_autocomplete.js.coffee @@ -0,0 +1,18 @@ +angular.module("admin.users").directive "ofnUserAutocomplete", ($http) -> + link: (scope,element,attrs) -> + setTimeout -> + element.select2 + multiple: false + initSelection: (element, callback) -> + callback { id: element.val(), email: attrs.email } + ajax: + url: Spree.routes.user_search + datatype: 'json' + data:(term, page) -> + { q: term } + results: (data, page) -> + { results: data } + formatResult: (user) -> + user.email + formatSelection: (user) -> + user.email \ No newline at end of file diff --git a/app/assets/javascripts/admin/users/users.js.coffee b/app/assets/javascripts/admin/users/users.js.coffee new file mode 100644 index 0000000000..6bfd47a894 --- /dev/null +++ b/app/assets/javascripts/admin/users/users.js.coffee @@ -0,0 +1 @@ +angular.module("admin.users", []) \ No newline at end of file diff --git a/app/assets/javascripts/darkswarm/controllers/authentication/signup_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/authentication/signup_controller.js.coffee index d15ef4141d..123079b6b5 100644 --- a/app/assets/javascripts/darkswarm/controllers/authentication/signup_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/authentication/signup_controller.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.controller "SignupCtrl", ($scope, $http, $location, AuthenticationService) -> +Darkswarm.controller "SignupCtrl", ($scope, $http, $window, $location, Redirections, AuthenticationService) -> $scope.path = "/signup" $scope.errors = email: null @@ -6,6 +6,9 @@ Darkswarm.controller "SignupCtrl", ($scope, $http, $location, AuthenticationServ $scope.submit = -> $http.post("/user/spree_user", {spree_user: $scope.spree_user}).success (data)-> - location.href = location.origin + location.pathname # Strips out hash fragments + if Redirections.after_login + $window.location.href = $window.location.origin + Redirections.after_login + else + $window.location.href = $window.location.origin + $window.location.pathname # Strips out hash fragments .error (data) -> $scope.errors = data diff --git a/app/assets/javascripts/darkswarm/controllers/authentication_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/authentication_controller.js.coffee index 7c45fd53d1..1a22f34b0c 100644 --- a/app/assets/javascripts/darkswarm/controllers/authentication_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/authentication_controller.js.coffee @@ -1,7 +1,7 @@ Darkswarm.controller "AuthenticationCtrl", ($scope, AuthenticationService, SpreeUser)-> $scope.open = AuthenticationService.open $scope.toggle = AuthenticationService.toggle - + $scope.spree_user = SpreeUser.spree_user $scope.active = AuthenticationService.active $scope.select = AuthenticationService.select diff --git a/app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee new file mode 100644 index 0000000000..2fcc0f32fd --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee @@ -0,0 +1,11 @@ +Darkswarm.controller "RegistrationCtrl", ($scope, RegistrationService, EnterpriseRegistrationService, availableCountries) -> + $scope.currentStep = RegistrationService.currentStep + $scope.enterprise = EnterpriseRegistrationService.enterprise + $scope.select = RegistrationService.select + + $scope.steps = ['details','address','contact','about','images','social'] + + $scope.countries = availableCountries + + $scope.countryHasStates = -> + $scope.enterprise.country.states.length > 0 diff --git a/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee new file mode 100644 index 0000000000..84f133da54 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee @@ -0,0 +1,15 @@ +Darkswarm.controller "RegistrationFormCtrl", ($scope, RegistrationService, EnterpriseRegistrationService) -> + $scope.submitted = false + + $scope.valid = (form) -> + $scope.submitted = !form.$valid + form.$valid + + $scope.create = (form) -> + EnterpriseRegistrationService.create() if $scope.valid(form) + + $scope.update = (nextStep, form) -> + EnterpriseRegistrationService.update(nextStep) if $scope.valid(form) + + $scope.selectIfValid = (nextStep, form) -> + RegistrationService.select(nextStep) if $scope.valid(form) \ No newline at end of file diff --git a/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee b/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee new file mode 100644 index 0000000000..46550b854f --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee @@ -0,0 +1,6 @@ +Darkswarm.directive "ofnInlineFlash", -> + restrict: 'E' + controller: ($scope) -> + $scope.visible = true + $scope.closeFlash = -> + $scope.visible = false diff --git a/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee b/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee new file mode 100644 index 0000000000..ffa98c5a33 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee @@ -0,0 +1,16 @@ +# Convert number to string currency using injected currency configuration. +# +# @requires currencyConfig json - /app/serializers/api/currency_config_serializer.rb +# @return: string +Darkswarm.filter "localizeCurrency", (currencyConfig)-> + (amount) -> + currency_code = if currencyConfig.display_currency then " " + currencyConfig.currency else "" + decimals = if currencyConfig.hide_cents == "true" then 0 else 2 + # We need to use parseFloat before toFixed as the amount should be a passed in as a string. + amount_fixed = parseFloat(amount).toFixed(decimals) + + # Build the final price string. + if currencyConfig.symbol_position == 'before' + currencyConfig.symbol + amount_fixed + currency_code + else + amount_fixed + " " + currencyConfig.symbol + currency_code diff --git a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee index ff7db62f4a..5f5ae520e3 100644 --- a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee +++ b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee @@ -4,12 +4,14 @@ Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redir selectedPath: "/login" constructor: -> - if $location.path() in ["/login", "/signup", "/forgot"] - @open() + if $location.path() in ["/login", "/signup", "/forgot"] && location.pathname isnt '/register/auth' + @open $location.path() + else if location.pathname is '/register/auth' + @open '/signup', 'registration_authentication.html' - open: (path = false)=> + open: (path = false, template = 'authentication.html') => @modalInstance = $modal.open - templateUrl: 'authentication.html' + templateUrl: template windowClass: "login-modal medium" @modalInstance.result.then @close, @close @selectedPath = path || @selectedPath diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index def1c9b6d0..2b58bb70ae 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -3,7 +3,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> new class Cart dirty: false order: CurrentOrder.order - line_items: CurrentOrder.order?.line_items || [] + line_items: CurrentOrder.order?.line_items || [] constructor: -> for line_item in @line_items line_item.variant.line_item = line_item @@ -22,13 +22,13 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> # TODO what shall we do here? data: => - variants = {} + variants = {} for li in @line_items_present() - variants[li.variant.id] = + variants[li.variant.id] = quantity: li.quantity max_quantity: li.max_quantity {variants: variants} - + saved: => @dirty = false @@ -48,15 +48,15 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> total: => @line_items_present().map (li)-> - li.variant.getPrice() + li.variant.totalPrice() .reduce (t, price)-> t + price , 0 register_variant: (variant)=> exists = @line_items.some (li)-> li.variant == variant - @create_line_item(variant) unless exists - + @create_line_item(variant) unless exists + create_line_item: (variant)-> variant.line_item = variant: variant diff --git a/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee new file mode 100644 index 0000000000..68915193ee --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee @@ -0,0 +1,57 @@ +Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, CurrentUser, spreeApiKey, Loading, availableCountries, enterpriseAttributes) -> + new class EnterpriseRegistrationService + enterprise: + user_ids: [CurrentUser.id] + email: CurrentUser.email + address: {} + country: availableCountries[0] + + constructor: -> + for key, value of enterpriseAttributes + @enterprise[key] = value + + create: => + Loading.message = "Creating " + @enterprise.name + $http( + method: "POST" + url: "/api/enterprises" + data: + enterprise: @prepare() + params: + token: spreeApiKey + ).success((data) => + Loading.clear() + @enterprise.id = data + RegistrationService.select('about') + ).error((data) => + Loading.clear() + alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.') + ) + # RegistrationService.select('about') + + update: (step) => + Loading.message = "Updating " + @enterprise.name + $http( + method: "PUT" + url: "/api/enterprises/#{@enterprise.id}" + data: + enterprise: @prepare() + params: + token: spreeApiKey + ).success((data) -> + Loading.clear() + RegistrationService.select(step) + ).error((data) -> + Loading.clear() + alert('Failed to update your enterprise.\nPlease ensure all fields are completely filled out.') + ) + # RegistrationService.select(step) + + prepare: => + enterprise = {} + excluded = [ 'address', 'country', 'id' ] + for key, value of @enterprise when key not in excluded + enterprise[key] = value + enterprise.address_attributes = @enterprise.address if @enterprise.address? + enterprise.address_attributes.country_id = @enterprise.country.id if @enterprise.country? + enterprise \ No newline at end of file diff --git a/app/assets/javascripts/darkswarm/services/registration_service.js.coffee b/app/assets/javascripts/darkswarm/services/registration_service.js.coffee new file mode 100644 index 0000000000..a2a1fe2dc4 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/registration_service.js.coffee @@ -0,0 +1,23 @@ +Darkswarm.factory "RegistrationService", (Navigation, $modal, Loading)-> + + new class RegistrationService + constructor: -> + @open() + + open: => + @modalInstance = $modal.open + templateUrl: 'registration.html' + windowClass: "login-modal large" + backdrop: 'static' + @modalInstance.result.then @close, @close + @select 'introduction' + + select: (step)=> + @current_step = step + + currentStep: => + @current_step + + close: -> + Loading.message = "Taking you back to the home page" + Navigation.go "/" \ No newline at end of file diff --git a/app/assets/javascripts/darkswarm/services/variants.js.coffee b/app/assets/javascripts/darkswarm/services/variants.js.coffee index 0f231ac030..bc2050e4d4 100644 --- a/app/assets/javascripts/darkswarm/services/variants.js.coffee +++ b/app/assets/javascripts/darkswarm/services/variants.js.coffee @@ -5,7 +5,7 @@ Darkswarm.factory 'Variants', -> @variants[variant.id] ||= @extend variant extend: (variant)-> - variant.getPrice = -> - variant.price * variant.line_item.quantity - variant.basePricePercentage = Math.round(variant.base_price / variant.price * 100) + variant.totalPrice = -> + variant.price_with_fees * variant.line_item.quantity + variant.basePricePercentage = Math.round(variant.price / variant.price_with_fees * 100) variant diff --git a/app/assets/javascripts/templates/partials/close.html.haml b/app/assets/javascripts/templates/partials/close.html.haml index 1facd8edeb..a02ae410a8 100644 --- a/app/assets/javascripts/templates/partials/close.html.haml +++ b/app/assets/javascripts/templates/partials/close.html.haml @@ -1,2 +1,2 @@ -%a.close-reveal-modal.outside{"ng-click" => "$close()"} +%a.close-reveal-modal{"ng-click" => "$close()"} %i.ofn-i_009-close diff --git a/app/assets/javascripts/templates/price_breakdown.html.haml b/app/assets/javascripts/templates/price_breakdown.html.haml index 0b7eba917a..cf82e31457 100644 --- a/app/assets/javascripts/templates/price_breakdown.html.haml +++ b/app/assets/javascripts/templates/price_breakdown.html.haml @@ -10,26 +10,26 @@ .expanded{"ng-show" => "expanded"} %ul %li.cost - .right {{ variant.base_price | currency }} + .right {{ variant.price | localizeCurrency }} Item cost %li{"bo-if" => "variant.fees.admin"} - .right {{ variant.fees.admin | currency }} + .right {{ variant.fees.admin | localizeCurrency }} Admin fee %li{"bo-if" => "variant.fees.sales"} - .right {{ variant.fees.sales | currency }} + .right {{ variant.fees.sales | localizeCurrency }} Sales fee %li{"bo-if" => "variant.fees.packing"} - .right {{ variant.fees.packing | currency }} + .right {{ variant.fees.packing | localizeCurrency }} Packing fee %li{"bo-if" => "variant.fees.transport"} - .right {{ variant.fees.transport | currency }} + .right {{ variant.fees.transport | localizeCurrency }} Transport fee %li{"bo-if" => "variant.fees.fundraising"} - .right {{ variant.fees.fundraising | currency }} + .right {{ variant.fees.fundraising | localizeCurrency }} Fundraising fee %li %strong - .right = {{ variant.price | currency }} + .right = {{ variant.price_with_fees | localizeCurrency }}   %a{"ng-click" => "expanded = !expanded"} diff --git a/app/assets/javascripts/templates/registration.html.haml b/app/assets/javascripts/templates/registration.html.haml new file mode 100644 index 0000000000..10a12d712e --- /dev/null +++ b/app/assets/javascripts/templates/registration.html.haml @@ -0,0 +1,10 @@ +%div#registration-modal{"ng-controller" => "RegistrationCtrl"} + %div{ ng: { show: "currentStep() == 'introduction'" } } + %ng-include{ src: "'registration/introduction.html'" } + %div{ ng: { repeat: 'step in steps', show: "currentStep() == step" } } + %ng-include{ src: "'registration/'+ step + '.html'" } + %div{ ng: { show: "currentStep() == 'finished'" } } + %ng-include{ src: "'registration/finished.html'" } + +%a.close-reveal-modal{"ng-click" => "$close()"} + %i.ofn-i_009-close diff --git a/app/assets/javascripts/templates/registration/about.html.haml b/app/assets/javascripts/templates/registration/about.html.haml new file mode 100644 index 0000000000..07e631345f --- /dev/null +++ b/app/assets/javascripts/templates/registration/about.html.haml @@ -0,0 +1,44 @@ +.container#registration-about + .header + %h2 Nice one! + %h5 + Now let's flesh out the details about + %span.brick{"ng-show" => "enterprise.is_distributor"} + {{ enterprise.name }} + %span.turquoise{"ng-show" => "!enterprise.is_distributor" } + {{ enterprise.name }} + + %ng-include{ src: "'registration/steps.html'" } + %form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('social',about)" } } + .row + .small-12.columns + .alert-box.alert{"data-alert" => ""} + {{ enterprise.name }} won't be visible on the Open Food Network until you enter a long and short description. + %a.close{:href => "#"} × + + .alert-box.info{"data-alert" => ""} + {{ enterprise.name }} has been created on the Open Food Network. If you leave at any point from here onwards, your enterprise will be saved, and you can always login to the admin section to update or continue filling out your enterprise details. + %a.close{:href => "#"} × + + .small-12.large-8.columns + .row + .small-12.columns + %label{ for: 'enterprise_description' } Short Description: + %input.chunky.small-12.columns{ id: 'enterprise_description', placeholder: "A short sentence describing your enterprise", ng: { model: 'enterprise.description' } } + .row + .small-12.columns + %label{ for: 'enterprise_long_desc' } Long Description: + %textarea.chunky.small-12.columns{ id: 'enterprise_long_desc', placeholder: "We recommend keeping your description to under 600 characters or 150 words. Why? Cus people are lazy, and don't like to read too much text online. ;)", ng: { model: 'enterprise.long_description' } } + %small {{ enterprise.long_description.length }} characters used + .small-12.large-4.columns + .row + .small-12.columns + %label{ for: 'enterprise_abn' } ABN: + %input.chunky.small-12.columns{ id: 'enterprise_abn', placeholder: "eg. 99 123 456 789", ng: { model: 'enterprise.abn' } } + .row + .small-12.columns + %label{ for: 'enterprise_acn' } ACN: + %input.chunky.small-12.columns{ id: 'enterprise_acn', placeholder: "eg. 123 456 789", ng: { model: 'enterprise.acn' } } + .row.buttons.pad-top + .small-12.columns + %input.button.primary{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/address.html.haml b/app/assets/javascripts/templates/registration/address.html.haml new file mode 100644 index 0000000000..6fe39e9285 --- /dev/null +++ b/app/assets/javascripts/templates/registration/address.html.haml @@ -0,0 +1,60 @@ +.container#registration-address + .header + %h2 + Greetings + %span{ ng: { class: "{brick: enterprise.is_distributor, turquoise: !enterprise.is_distributor}" } } + {{ enterprise.name }} + + %h5 Now we need to know where you are + %ng-include{ src: "'registration/steps.html'" } + %form{ name: 'address', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('contact',address)" } } + .row.content + .small-12.medium-12.large-7.columns + .row + .small-12.columns.field + %label{ for: 'enterprise_address' } Address: + %input.chunky.small-12.columns{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "eg. 123 Cranberry Drive", required: true, ng: { model: 'enterprise.address.address1' } } + %span.error.small-12.columns{ ng: { show: "address.address1.$error.required && submitted" } } + You need to enter an address. + .row + .small-12.large-8.columns.field + %label{ for: 'enterprise_city' } Suburb: + %input.chunky.small-12.columns{ id: 'enterprise_city', name: 'city', required: true, placeholder: "eg. Northcote", ng: { model: 'enterprise.address.city' } } + %span.error.small-12.columns{ ng: { show: "address.city.$error.required && submitted" } } + You need to enter a suburb. + .small-12.large-4.columns.field + %label{ for: 'enterprise_zipcode' } Postcode: + %input.chunky.small-12.columns{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "eg. 3070", ng: { model: 'enterprise.address.zipcode' } } + %span.error.small-12.columns{ ng: { show: "address.zipcode.$error.required && submitted" } } + You need to enter a postcode. + .row + .small-12.large-8.columns.field + %label{ for: 'enterprise_country' } Country: + %select.chunky.small-12.columns{ id: 'enterprise_country', name: 'country', required: true, ng: { model: 'enterprise.country', options: 'c as c.name for c in countries' } } + %span.error.small-12.columns{ ng: { show: "address.country.$error.required && submitted" } } + You need to enter a country. + .small-12.large-4.columns.field + %label{ for: 'enterprise_state' } State: + %select.chunky.small-12.columns{ id: 'enterprise_state', name: 'state', ng: { model: 'enterprise.address.state_id', options: 's.id as s.abbr for s in enterprise.country.states', show: 'countryHasStates()', required: 'countryHasStates()' } } + %span.error.small-12.columns{ ng: { show: "address.state.$error.required && submitted" } } + You need to enter a state. + .small-12.medium-12.large-5.hide-for-small-only + // This is the location area + / %h6 + / Location display + / %i.ofn-i_013-help.has-tip{ 'data-tooltip' => true, title: "Choose how you want to display your enterprise's address on the Open Food Network. By default, full location is shown everywhere including street name and number."} + / .row + / .small-12.columns + / %label.indent-checkbox + / %input{ type: 'checkbox', id: 'enterpise_suburb_only', ng: { model: 'enterprise.suburb_only' } } + / Hide my street name and street number from the public (ie. only show the suburb) + / .small-12.columns + / %label.indent-checkbox + / %input{ type: 'checkbox', id: 'enterprise_on_map', ng: { model: 'enterprise.on_map' } } + / Blur my location on the map (show an approximate, not exact pin) + + .row.buttons + .small-12.columns + %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('details')" } } +   + %input.button.primary{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/contact.html.haml b/app/assets/javascripts/templates/registration/contact.html.haml new file mode 100644 index 0000000000..c7262248d0 --- /dev/null +++ b/app/assets/javascripts/templates/registration/contact.html.haml @@ -0,0 +1,46 @@ +.container#registration-contact + .header + %h2 Last step to create your enterprise! + %h5 + Who is responsible for managing + %span{ ng: { class: "{brick: enterprise.is_distributor, turquoise: !enterprise.is_distributor}" } } + {{ enterprise.name }}? + %ng-include{ src: "'registration/steps.html'" } + %form{ name: 'contact', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "create(contact)" } } + .row.content + .small-12.medium-12.large-7.columns + .row + .small-12.columns.field + %label{ for: 'enterprise_contact' } Primary Contact: + %input.chunky.small-12.columns{ id: 'enterprise_contact', name: 'contact', required: true, placeholder: "Contact Name", ng: { model: 'enterprise.contact' } } + %span.error.small-12.columns{ ng: { show: "contact.contact.$error.required && submitted" } } + You need to enter a primary contact. + .row + .small-12.columns.field + %label{ for: 'enterprise_email' } Email address: + %input.chunky.small-12.columns{ id: 'enterprise_email', name: 'email', type: 'email', required: true, placeholder: "eg. charlie@thefarm.com", ng: { model: 'enterprise.email' } } + %span.error.small-12.columns{ ng: { show: "(contact.email.$error.email || contact.email.$error.required) && submitted" } } + You need to enter valid email address. + .row + .small-12.columns.field + %label{ for: 'enterprise_phone' } Phone number: + %input.chunky.small-12.columns{ id: 'enterprise_phone', name: 'phone', placeholder: "eg. (03) 1234 5678", ng: { model: 'enterprise.phone' } } + .small-12.medium-12.large-5.hide-for-small-only + / %h6 + / Contact display + / %i.ofn-i_013-help.has-tip{ 'data-tooltip' => true, title: "Choose how you want to display your contact details on the Open Food Network."} + / .row + / .small-12.columns + / %label.indent-checkbox + / %input{ type: 'checkbox', id: 'contact_name_profile', ng: { model: 'enterprise.name_in_profile' } }   Display name in profile + / .small-12.columns + / %label.indent-checkbox + / %input{ type: 'checkbox', id: 'contact_email_profile', ng: { model: 'enterprise.email_in_profile' } }   Display email in profile + / .small-12.columns + / %label.indent-checkbox + / %input{ type: 'checkbox', id: 'contact_phone_profile', ng: { model: 'enterprise.phone_in_profile' } }   Display phone in profile + .row.buttons + .small-12.columns + %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('address')" } } +   + %input.button.primary{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/details.html.haml b/app/assets/javascripts/templates/registration/details.html.haml new file mode 100644 index 0000000000..bb358a1864 --- /dev/null +++ b/app/assets/javascripts/templates/registration/details.html.haml @@ -0,0 +1,42 @@ +.container#registration-details{bindonce: true} + .header + %h2 Let's Get Started + %h5{ bo: { if: "enterprise.type != 'single'" } } Woot! First we need to know what sort of enterprise you are: + %h5{ bo: { if: "enterprise.type == 'single'" } } Woot! First we need to know the name of your farm: + %ng-include{ src: "'registration/steps.html'" } + %form{ name: 'details', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('address',details)" } } + .row + .small-12.columns.field + %label{ for: 'enterprise_name', bo: { if: "enterprise.type != 'single'" } } Enterprise Name: + %label{ for: 'enterprise_name', bo: { if: "enterprise.type == 'single'" } } Farm Name: + %input.chunky.small-12.columns{ id: 'enterprise_name', name: 'name', placeholder: "eg. Charlie's Awesome Farm", required: true, ng: { model: 'enterprise.name' } } + %span.error.small-12.columns{ ng: { show: "details.name.$error.required && submitted" } } + You need to enter a name for your enterprise! + + .row#enterprise-types{ 'data-equalizer' => true, bo: { if: "enterprise.type != 'single'" } } + .small-12.columns.field + .row + .small-12.columns + %label Choose one: + .row + .small-12.medium-4.large-4.columns{ 'data-equalizer-watch' => true } + %a.panel#producer-panel{ href: "#", ng: { click: "enterprise.is_distributor = false; enterprise.is_primary_producer = true", class: "{selected: (!enterprise.is_distributor && enterprise.is_primary_producer)}" } } + .left + / %render-svg{ path: "/assets/map-icon-producer.svg" } + %h4 I'm A Producer + %p Producers make yummy things to eat &/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it. + .small-12.medium-4.large-4.columns{ 'data-equalizer-watch' => true } + %a.panel#hub-panel{ href: "#", ng: { click: "enterprise.is_distributor = true; enterprise.is_primary_producer = false", class: "{selected: (enterprise.is_distributor && !enterprise.is_primary_producer)}" } } + .left + / %render-svg{ path: "/assets/map-icon-hub.svg" } + %h4 I'm A Hub + %p Hubs connect the producer to the eater. Hubs can be co-ops, independent retailers, buying groups, wholesalers, CSA box schemes, farm-gate stalls, etc. + .small-12.medium-4.large-4.columns{ 'data-equalizer-watch' => true } + %a.panel#both-panel{ href: "#", ng: { click: "enterprise.is_distributor = true; enterprise.is_primary_producer = true", class: "{selected: (enterprise.is_distributor && enterprise.is_primary_producer)}" } } + .left + / %render-svg{path: "/assets/map-icon-both.svg"} + %h4 I'm Both + %p Hey there, Jack-of-all-trades! Not only do you produce things to eat &/or drink, you also want to sell your yummies through an Open Food Network shopfront. + .row.buttons + .small-12.columns + %input.button.primary.right{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/finished.html.haml b/app/assets/javascripts/templates/registration/finished.html.haml new file mode 100644 index 0000000000..75489c607a --- /dev/null +++ b/app/assets/javascripts/templates/registration/finished.html.haml @@ -0,0 +1,18 @@ +.container#registration-finished + .header + %h2 Well done! + %h5 + You have successfully completed the profile for + %span.brick{"ng-show" => "enterprise.is_distributor"} + {{ enterprise.name }} + %span.turquoise{"ng-show" => "!enterprise.is_distributor" } + {{ enterprise.name }} + .content{ style: 'text-align: center'} + %h3 Why not check it out on the Open Food Network? + %a.button.primary{ type: "button", href: "/map" } Go to Map Page > + + %br + %br + + %h3 Next step - add some products: + %a.button.primary{ type: "button", href: "/admin/products/new" } Add a Product > diff --git a/app/assets/javascripts/templates/registration/images.html.haml b/app/assets/javascripts/templates/registration/images.html.haml new file mode 100644 index 0000000000..efd3688076 --- /dev/null +++ b/app/assets/javascripts/templates/registration/images.html.haml @@ -0,0 +1,14 @@ +.container#registration-images + .header + %h2 Thanks! + %h5 Let's upload some pretty pictures so your profile looks great! :) + %ng-include{ src: "'registration/steps.html'" } + .row.content + + .row.buttons + .small-12.columns + %input.button.primary.left{ type: "button", value: "Back", ng: { click: "select('about')" } } +   + %input.button.primary.right{ type: "button", value: "Continue", ng: { click: "select('social')" } } + + \ No newline at end of file diff --git a/app/assets/javascripts/templates/registration/introduction.html.haml b/app/assets/javascripts/templates/registration/introduction.html.haml new file mode 100644 index 0000000000..8a4f4f7e02 --- /dev/null +++ b/app/assets/javascripts/templates/registration/introduction.html.haml @@ -0,0 +1,40 @@ +%div + .header + %h2 Hi there! + %h4 This wizard will step you through creating a profile + .row + .small-12.medium-3.large-2.columns.text-right.hide-for-small-only + %img{:src => "/assets/potatoes.png"} + .small-12.medium-9.large-10.columns + %p + Your profile gives you an online presence on the + %strong Open Food Network, + allowing you to easily connect with potential customers or partners. You can always choose to update your info later, as well as choose to upgrade your Profile to and Online Store, where you can sell products, track orders and receive payments. Creating a profile takes about 5-10 minutes. + .row{ 'data-equalizer' => true } + .small-12.medium-6.large-6.columns.pad-top{ 'data-equalizer-watch' => true } + %h5 You'll need the following: + %ul.check-list + %li + Your enterprise address and contact details + %li + Your logo image + %li + A pretty picture for your profile header + %li + Some 'About Us' text + + .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true} + .highlight-box + %h5 Your profile entitles you to: + %ul.small-block-grid-1 + %li + %i.ofn-i_020-search + A searchable listing + %li + %i.ofn-i_040-hub + A pin on the OFN map + .row + .small-12.columns + %hr + %input.button.primary{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } } + \ No newline at end of file diff --git a/app/assets/javascripts/templates/registration/social.html.haml b/app/assets/javascripts/templates/registration/social.html.haml new file mode 100644 index 0000000000..2aa12bd08b --- /dev/null +++ b/app/assets/javascripts/templates/registration/social.html.haml @@ -0,0 +1,35 @@ +.container#registration-social + .header + %h2 Last step! + %h5 How can people find {{ enterprise.name }} online? + %ng-include{ src: "'registration/steps.html'" } + %form{ name: 'social', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('finished',social)" } } + .row.content + .small-12.large-7.columns + .row + .small-12.columns + %label{ for: 'enterprise_website' } Website: + %input.chunky.small-12.columns{ id: 'enterprise_website', placeholder: "eg. openfoodnetwork.org.au", ng: { model: 'enterprise.website' } } + .row + .small-12.columns + %label{ for: 'enterprise_facebook' } Facebook: + %input.chunky.small-12.columns{ id: 'enterprise_facebook', placeholder: "eg. www.facebook.com/PageNameHere", ng: { model: 'enterprise.facebook' } } + .row + .small-12.columns + %label{ for: 'enterprise_linkedin' } LinkedIn: + %input.chunky.small-12.columns{ id: 'enterprise_linkedin', placeholder: "eg. www.linkedin.com/YourNameHere", ng: { model: 'enterprise.linkedin' } } + .small-12.large-5.columns + .row + .small-12.columns + %label{ for: 'enterprise_twitter' } Twitter: + %input.chunky.small-12.columns{ id: 'enterprise_twitter', placeholder: "eg. @twitter_handle", ng: { model: 'enterprise.twitter' } } + .row + .small-12.columns + %label{ for: 'enterprise_instagram' } Instagram: + %input.chunky.small-12.columns{ id: 'enterprise_instagram', placeholder: "eg. @instagram_handle", ng: { model: 'enterprise.instagram' } } + + .row.buttons + .small-12.columns + %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('about')" } } +   + %input.button.primary{ type: "submit", value: "Continue" } \ No newline at end of file diff --git a/app/assets/javascripts/templates/registration/steps.html.haml b/app/assets/javascripts/templates/registration/steps.html.haml new file mode 100644 index 0000000000..65e3cb6b48 --- /dev/null +++ b/app/assets/javascripts/templates/registration/steps.html.haml @@ -0,0 +1,5 @@ +.row#progress-bar + .small-12.medium-2.columns.item{ ng: { repeat: 'step in steps', class: "{active: (currentStep() == step),'show-for-medium-up': (currentStep() != step)}" } } + {{ $index+1 + ". " + step }} + + \ No newline at end of file diff --git a/app/assets/javascripts/templates/registration_authentication.html.haml b/app/assets/javascripts/templates/registration_authentication.html.haml new file mode 100644 index 0000000000..c7bfbabeca --- /dev/null +++ b/app/assets/javascripts/templates/registration_authentication.html.haml @@ -0,0 +1,17 @@ +.container + .row.modal-centered + %h2 Welcome to the Open Food Network! + %h5 Start By Signing Up (or logging in): + %div{"ng-controller" => "AuthenticationCtrl"} + %tabset + %ng-include{src: "'signup.html'"} + %ng-include{src: "'login.html'"} + %ng-include{src: "'forgot.html'"} + %div{ ng: { show: "active('/signup')"} } + %hr + Already have an account? + %a{ href: "", ng: { click: "select('/login')"}} + Log in now. + +%a.close-reveal-modal{"ng-click" => "$close()"} + %i.ofn-i_009-close diff --git a/app/assets/javascripts/templates/shop_variant.html.haml b/app/assets/javascripts/templates/shop_variant.html.haml index a6db8b4e41..b1538bb69e 100644 --- a/app/assets/javascripts/templates/shop_variant.html.haml +++ b/app/assets/javascripts/templates/shop_variant.html.haml @@ -48,7 +48,7 @@ .small-4.medium-2.large-2.columns.variant-price .table-cell.price %i.ofn-i_009-close - {{ variant.price | currency }} + {{ variant.price_with_fees | localizeCurrency }} -# Now in a template in app/assets/javascripts/templates ! %price-breakdown{"price-breakdown" => "_", variant: "variant", @@ -59,4 +59,4 @@ .small-12.medium-2.large-2.columns.total-price.text-right .table-cell %strong - {{ variant.getPrice() | currency }} + {{ variant.totalPrice() | localizeCurrency }} diff --git a/app/assets/javascripts/templates/signup.html.haml b/app/assets/javascripts/templates/signup.html.haml index db066685f1..28bec19b49 100644 --- a/app/assets/javascripts/templates/signup.html.haml +++ b/app/assets/javascripts/templates/signup.html.haml @@ -1,12 +1,12 @@ -%tab#sign-up-content{"ng-controller" => "SignupCtrl", - heading: "Sign up", +%tab#sign-up-content{"ng-controller" => "SignupCtrl", + heading: "Sign up", active: "active(path)", select: "select(path)"} %form{"ng-submit" => "submit()"} .row .large-12.columns %label{for: "email"} Your email - %input.title.input-text{name: "email", + %input.title.input-text{name: "email", type: "email", id: "email", tabindex: 1, @@ -16,7 +16,7 @@ .row .large-12.columns %label{for: "password"} Choose a password - %input.title.input-text{name: "password", + %input.title.input-text{name: "password", type: "password", id: "password", autocomplete: "off", @@ -27,7 +27,7 @@ .row .large-12.columns %label{for: "password_confirmation"} Confirm password - %input.title.input-text{name: "password_confirmation", + %input.title.input-text{name: "password_confirmation", type: "password", id: "password_confirmation", autocomplete: "off", @@ -35,7 +35,7 @@ "ng-model" => "spree_user.password_confirmation"} .row .large-12.columns - %input.button.primary{name: "commit", - tabindex: "3", - type: "submit", + %input.button.primary{name: "commit", + tabindex: "3", + type: "submit", value: "Sign up now"} diff --git a/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass b/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass index e1eb2cd448..feb6529fa2 100644 --- a/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass @@ -84,6 +84,9 @@ button.graph-button display: inline background-color: rgba(255,255,255,0.5) padding: 5px + @media all and (max-width: 768px) + display: none + // Hide for small &:hover, &:active, &:focus background-color: rgba(255,255,255,1) diff --git a/app/assets/stylesheets/darkswarm/active_table_search.css.sass b/app/assets/stylesheets/darkswarm/active_table_search.css.sass index 417b0d9996..0503e30d0f 100644 --- a/app/assets/stylesheets/darkswarm/active_table_search.css.sass +++ b/app/assets/stylesheets/darkswarm/active_table_search.css.sass @@ -9,7 +9,13 @@ margin-right: 0 .row.filter-box:first-child - // border-top: 1px solid $clr-brick + border: 1px solid $clr-blue-light + @include border-radius(0.25em) + margin-top: 2px + +.row.filter-box:last-child + background: transparent + margin-top: 1em products .filter-box background: #f7f7f7 @@ -20,6 +26,8 @@ products .filter-box .tdhead padding: 0.25rem 0.5rem margin-top: 0.9rem + color: $clr-blue + border-bottom: 1px solid $clr-blue-light // OVERRIDES [class*="block-grid-"] diff --git a/app/assets/stylesheets/darkswarm/big-input.sass b/app/assets/stylesheets/darkswarm/big-input.sass index 20c87d6a6b..15bf937f39 100644 --- a/app/assets/stylesheets/darkswarm/big-input.sass +++ b/app/assets/stylesheets/darkswarm/big-input.sass @@ -9,8 +9,6 @@ @include avenir @include csstrans @include border-radius(0.5rem) - - // transition: all 0.5s ease background: rgba(255,255,255,0.1) border: 2px solid $input font-size: 2rem @@ -21,6 +19,8 @@ margin-bottom: 0.5rem box-shadow: none color: $inputactv + @media all and (max-width: 640px) + font-size: 1.25rem &:hover @include box-shadow(0 1px 1px 0 rgba(255,255,255,0.25)) diff --git a/app/assets/stylesheets/darkswarm/branding.css.sass b/app/assets/stylesheets/darkswarm/branding.css.sass index 9fa27dcc96..011ef2ff68 100644 --- a/app/assets/stylesheets/darkswarm/branding.css.sass +++ b/app/assets/stylesheets/darkswarm/branding.css.sass @@ -11,8 +11,13 @@ $clr-turquoise-light: #ceefe4 $clr-turquoise-ultra-light: #e8f9f4 $clr-turquoise-bright: #23a877 +$clr-blue: #0096ad +$clr-blue-light: #85d9e5 +$clr-blue-bright: #14b6cc + $disabled-light: #e5e5e5 $disabled-bright: #ccc $disabled-dark: #999 $med-grey: #666 -$dark-grey: #333 \ No newline at end of file +$dark-grey: #333 + diff --git a/app/assets/stylesheets/darkswarm/modals.css.sass b/app/assets/stylesheets/darkswarm/modals.css.sass index 0681dd73b4..e6303a4cd7 100644 --- a/app/assets/stylesheets/darkswarm/modals.css.sass +++ b/app/assets/stylesheets/darkswarm/modals.css.sass @@ -6,27 +6,24 @@ dialog, .reveal-modal outline: none padding: 1rem overflow-y: scroll - - // Sets up max heights based on device height - @media all and (min-height: 1025px) + + // Reveal.js break point: + // @media only screen and (max-width: 40.063em) + + // Small - when modal IS full screen + @media only screen and (max-width: 640px) + max-height: initial + // This is needed to make the height not the height of whole content page + min-height: 100% + position: absolute !important + top: 0 + left: 0 + + // Medium and up - when modal IS NOT full screen + @media only screen and (min-width: 641px) + top: 10% max-height: 80% - @media all and (min-height: 700px) and (max-height: 1024px) - max-height: 70% - - @media all and (min-height: 600px) and (max-height: 699px) - max-height: 60% - - @media all and (min-height: 481px) and (max-height: 599px) - max-height: 60% - - @media only screen and (max-height: 480px) and (min-width: 641px) - max-height: 60% - - @media all and (max-height: 480px) - overflow-y: scroll - - .reveal-modal-bg background-color: rgba(0,0,0,0.65) @@ -39,31 +36,3 @@ dialog .close-reveal-modal, .reveal-modal .close-reveal-modal &:hover, &:active, &:focus background-color: rgba(235,235,235,1) color: #333 - -// dialog .close-reveal-modal.outside, .reveal-modal .close-reveal-modal.outside -// top: -2.5rem -// right: -2.5rem -// font-size: 2rem -// color: white -// text-shadow: none -// padding: 0.25rem -// @include border-radius(999999) -// border: 1px solid transparent -// &:hover, &:active, &:focus -// text-shadow: 0 1px 3px #333 -// border: 1px solid white - -// @media all and (max-width: 640px) -// top: 0.5rem -// right: 0.5rem -// font-size: 2rem -// color: white -// text-shadow: none -// padding: 0.25rem -// background-color: rgba(150,150,150,0.85) -// @include border-radius(999999) -// border: 1px solid transparent -// &:hover, &:active, &:focus -// text-shadow: 0 1px 3px #333 -// border: 1px solid white - diff --git a/app/assets/stylesheets/darkswarm/registration.css.sass b/app/assets/stylesheets/darkswarm/registration.css.sass new file mode 100644 index 0000000000..3e851709bc --- /dev/null +++ b/app/assets/stylesheets/darkswarm/registration.css.sass @@ -0,0 +1,115 @@ +@import branding +@import mixins + +#registration-modal + .header + text-align: center + background-color: #efefef + padding-bottom: 1rem + .container + background-color: #ffffff + .content + // margin-bottom: 15px + + i + font-size: 150% + + .buttons + + ofn-inline-flash + display: block + padding: 15px + position: relative + margin-bottom: 10px + &.brick + background-color: $clr-brick-light + border: 2px solid $clr-brick + color: $clr-brick + &.turquoise + background-color: $clr-turquoise-light + border: 2px solid $clr-turquoise + color: $clr-turquoise + .close-button + position: absolute + top: 0px + right: 0px + + .field + margin-bottom: 15px + + input.chunky + padding: 8px + font-size: 105% + + label.indent-checkbox + display: block + padding-left: 20px + text-indent: -17px + input + margin: 0px + + label + margin-bottom: 3px + + ol, ul + // font-size: 80% + font-size: 0.875rem + padding: 0 + margin: 0 + ol + list-style-type: decimal + + .highlight-box + background: white + padding: 1rem 1.2rem + @media all and (max-width: 640px) + margin-top: 1rem + + #progress-bar + margin-bottom: 15px + .item + padding: 12px 0px + text-transform: uppercase + text-align: center + background-color: #333 + border: 2px solid #333 + color: #fff + .item.active + background-color: #cccccc + border: 2px solid #333 + color: #333 + @include box-shadow(inset 0 0 1px 0 #fff) + +#registration-details + #enterprise-types + a.panel + display: block + background-color: #efefef + color: black + @media all and (min-width: 768px) + min-height: 200px + &:hover + background-color: #fff + &#producer-panel:hover + &, & * + color: $clr-turquoise + &#hub-panel:hover, &#both-panel:hover + &, & * + color: $clr-brick + &.selected + &, & * + color: #fff + &#hub-panel, &#both-panel + background-color: $clr-brick-bright + &:hover + &, & * + color: white + &#producer-panel + background-color: $clr-turquoise-bright + &:hover + &, & * + color: white + p + clear: both + font-size: 0.875rem + diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index b49ce867c1..eb1885d361 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -48,6 +48,9 @@ small, .small .turquoise color: $clr-turquoise +.brick + color: $clr-brick + @mixin avenir font-family: "AvenirBla_IE", "AvenirBla" @@ -55,8 +58,8 @@ h1, h2, h3, h4, h5, h6, .avenir @include avenir padding: 0px -ul.bullet-list - margin: 0 +ul.bullet-list, ul.check-list + margin: 0 0 0 1.25em !important li list-style: none line-height: 1.5 @@ -64,12 +67,16 @@ ul.bullet-list li:before content: "\e609" font-family: "OFN" + margin-left: -1.25em display: inline-block font-weight: normal font-style: normal font-variant: normal text-transform: none - + +ul.check-list + li:before + content: "\e632" .light-grey color: #666666 diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index ceeb26abfc..64ba997950 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -3,9 +3,10 @@ module Admin before_filter :load_enterprise_set, :only => :index before_filter :load_countries, :except => :index before_filter :load_methods_and_fees, :only => [:new, :edit, :update, :create] - create.after :grant_management before_filter :check_type, only: :update before_filter :check_bulk_type, only: :bulk_update + before_filter :override_owner, only: :create + before_filter :check_owner, only: :update helper 'spree/products' include OrderCyclesHelper @@ -39,14 +40,6 @@ module Admin private - # When an enterprise user creates another enterprise, it is granted management - # permission for it - def grant_management - unless spree_current_user.has_spree_role? 'admin' - spree_current_user.enterprise_roles.create(enterprise: @object) - end - end - def load_enterprise_set @enterprise_set = EnterpriseSet.new :collection => collection end @@ -81,6 +74,16 @@ module Admin params[:enterprise].delete :type unless spree_current_user.admin? end + def override_owner + params[:enterprise][:owner_id] = spree_current_user.id unless spree_current_user.admin? + end + + def check_owner + unless spree_current_user == @enterprise.owner || spree_current_user.admin? + params[:enterprise].delete :owner_id + end + end + # Overriding method on Spree's resource controller def location_after_save if params[:enterprise].key? :producer_properties_attributes diff --git a/app/controllers/api/enterprises_controller.rb b/app/controllers/api/enterprises_controller.rb index 3dee7962c4..9a3b715093 100644 --- a/app/controllers/api/enterprises_controller.rb +++ b/app/controllers/api/enterprises_controller.rb @@ -1,5 +1,8 @@ module Api class EnterprisesController < Spree::Api::BaseController + + before_filter :override_owner, only: [:create, :update] + before_filter :check_type, only: :update respond_to :json def managed @@ -11,5 +14,37 @@ module Api @enterprises = Enterprise.ransack(params[:q]).result.accessible_by(current_api_user) render params[:template] || :bulk_index end + + def create + authorize! :create, Enterprise + + @enterprise = Enterprise.new(params[:enterprise]) + if @enterprise.save + render text: @enterprise.id, :status => 201 + else + invalid_resource!(@enterprise) + end + end + + def update + authorize! :update, Enterprise + + @enterprise = Enterprise.find(params[:id]) + if @enterprise.update_attributes(params[:enterprise]) + render text: @enterprise.id, :status => 200 + else + invalid_resource!(@enterprise) + end + end + + private + + def override_owner + params[:enterprise][:owner_id] = current_api_user.id + end + + def check_type + params[:enterprise].delete :type unless current_api_user.admin? + end end end diff --git a/app/controllers/registration_controller.rb b/app/controllers/registration_controller.rb new file mode 100644 index 0000000000..d58b10bd0b --- /dev/null +++ b/app/controllers/registration_controller.rb @@ -0,0 +1,25 @@ +require 'open_food_network/spree_api_key_loader' + +class RegistrationController < BaseController + include OpenFoodNetwork::SpreeApiKeyLoader + before_filter :load_spree_api_key, only: :index + before_filter :check_user, except: :authenticate + layout 'registration' + + def index + @enterprise_attributes = { type: 'profile' } + end + + def store + @enterprise_attributes = { is_distributor: true, is_primary_producer: true, type: 'single' } + render :index + end + + private + + def check_user + if spree_current_user.nil? + redirect_to registration_auth_path(anchor: "signup?after_login=#{request.env['PATH_INFO']}") + end + end +end diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index 27d9a36943..3e3c255ab3 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -1,4 +1,7 @@ +require 'open_food_network/spree_api_key_loader' + Spree::Admin::OrdersController.class_eval do + include OpenFoodNetwork::SpreeApiKeyLoader before_filter :load_spree_api_key, :only => :bulk_management # We need to add expections for collection actions other than :index here @@ -14,11 +17,4 @@ Spree::Admin::OrdersController.class_eval do page(params[:page]). per(params[:per_page] || Spree::Config[:orders_per_page]) } } } - - private - - def load_spree_api_key - current_user.generate_spree_api_key! unless spree_current_user.spree_api_key - @spree_api_key = spree_current_user.spree_api_key - end end diff --git a/app/controllers/spree/admin/products_controller_decorator.rb b/app/controllers/spree/admin/products_controller_decorator.rb index a5b38126e5..4c9c32dbae 100644 --- a/app/controllers/spree/admin/products_controller_decorator.rb +++ b/app/controllers/spree/admin/products_controller_decorator.rb @@ -1,5 +1,9 @@ +require 'open_food_network/spree_api_key_loader' + Spree::Admin::ProductsController.class_eval do - before_filter :load_bpe_data, :only => :bulk_edit + include OpenFoodNetwork::SpreeApiKeyLoader + before_filter :load_form_data, :only => [:bulk_edit, :new, :edit] + before_filter :load_spree_api_key, :only => :bulk_edit alias_method :location_after_save_original, :location_after_save @@ -85,9 +89,7 @@ Spree::Admin::ProductsController.class_eval do private - def load_bpe_data - current_user.generate_spree_api_key! unless spree_current_user.spree_api_key - @spree_api_key = spree_current_user.spree_api_key + def load_form_data @producers = OpenFoodNetwork::Permissions.new(spree_current_user).managed_product_enterprises.is_primary_producer.by_name @taxons = Spree::Taxon.order(:name) end diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 9ce62b2719..7fec829fe7 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -37,7 +37,9 @@ module Admin admin_inject_json_ams_array "ofn.admin", "users", @users, Api::Admin::UserSerializer end - + def admin_inject_spree_api_key + render partial: "admin/json/injection_ams", locals: {ngModule: 'ofn.admin', name: 'SpreeApiKey', json: "'#{@spree_api_key.to_s}'"} + end def admin_inject_json_ams(ngModule, name, data, serializer, opts = {}) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index c639d22104..046dd5d0eb 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -2,18 +2,18 @@ module InjectionHelper def inject_enterprises inject_json_ams "enterprises", Enterprise.all, Api::EnterpriseSerializer, active_distributors: @active_distributors end - + def inject_current_order inject_json_ams "currentOrder", current_order, Api::CurrentOrderSerializer, current_distributor: current_distributor, current_order_cycle: current_order_cycle end def inject_available_shipping_methods - inject_json_ams "shippingMethods", available_shipping_methods, + inject_json_ams "shippingMethods", available_shipping_methods, Api::ShippingMethodSerializer, current_order: current_order end def inject_available_payment_methods - inject_json_ams "paymentMethods", current_order.available_payment_methods, + inject_json_ams "paymentMethods", current_order.available_payment_methods, Api::PaymentMethodSerializer end @@ -21,6 +21,22 @@ module InjectionHelper inject_json_ams "taxons", Spree::Taxon.all, Api::TaxonSerializer end + def inject_currency_config + inject_json_ams "currencyConfig", {}, Api::CurrencyConfigSerializer + end + + def inject_spree_api_key + render partial: "json/injection_ams", locals: {name: 'spreeApiKey', json: "'#{@spree_api_key.to_s}'"} + end + + def inject_available_countries + inject_json_ams "availableCountries", available_countries, Api::CountrySerializer + end + + def inject_enterprise_attributes + render partial: "json/injection_ams", locals: {name: 'enterpriseAttributes', json: "#{@enterprise_attributes.to_json}"} + end + def inject_json(name, partial, opts = {}) render partial: "json/injection", locals: {name: name, partial: partial}.merge(opts) end diff --git a/app/helpers/spree/orders_helper.rb b/app/helpers/spree/orders_helper.rb index b72fb43db5..93b59c8e4b 100644 --- a/app/helpers/spree/orders_helper.rb +++ b/app/helpers/spree/orders_helper.rb @@ -8,7 +8,7 @@ module Spree def order_distribution_subtotal(order, options={}) options.reverse_merge! :format_as_currency => true amount = order.adjustments.enterprise_fee.sum &:amount - options.delete(:format_as_currency) ? number_to_currency(amount) : amount + options.delete(:format_as_currency) ? spree_number_to_currency(amount) : amount end def alternative_available_distributors(order) diff --git a/app/helpers/spree/products_helper_decorator.rb b/app/helpers/spree/products_helper_decorator.rb index f918a47649..22740218e1 100644 --- a/app/helpers/spree/products_helper_decorator.rb +++ b/app/helpers/spree/products_helper_decorator.rb @@ -1,8 +1,9 @@ module Spree ProductsHelper.class_eval do - # Return the price of the variant + # Return the price of the variant, overriding sprees price diff capability. + # This will allways return the variant price as if the show_variant_full_price is set. def variant_price_diff(variant) - "(#{number_to_currency variant.price})" + "(#{Spree::Money.new(variant.price).to_s})" end diff --git a/app/helpers/spree_currency_helper.rb b/app/helpers/spree_currency_helper.rb new file mode 100644 index 0000000000..479ced2771 --- /dev/null +++ b/app/helpers/spree_currency_helper.rb @@ -0,0 +1,5 @@ +module SpreeCurrencyHelper + def spree_number_to_currency(amount) + Spree::Money.new(amount).to_s + end +end diff --git a/app/mailers/enterprise_mailer.rb b/app/mailers/enterprise_mailer.rb new file mode 100644 index 0000000000..73c09e1e56 --- /dev/null +++ b/app/mailers/enterprise_mailer.rb @@ -0,0 +1,12 @@ +class EnterpriseMailer < Spree::BaseMailer + def creation_confirmation(enterprise) + find_enterprise(enterprise) + subject = "#{@enterprise.name} is now on #{Spree::Config[:site_name]}" + mail(:to => @enterprise.owner.email, :from => from_address, :subject => subject) + end + + private + def find_enterprise(enterprise) + @enterprise = enterprise.is_a?(Enterprise) ? enterprise : Enterprise.find(enterprise) + end +end diff --git a/app/mailers/spree/order_mailer_decorator.rb b/app/mailers/spree/order_mailer_decorator.rb index cae4de0e79..a43399bd95 100644 --- a/app/mailers/spree/order_mailer_decorator.rb +++ b/app/mailers/spree/order_mailer_decorator.rb @@ -1,6 +1,7 @@ Spree::OrderMailer.class_eval do helper HtmlHelper helper CheckoutHelper + helper SpreeCurrencyHelper def confirm_email(order, resend = false) find_order(order) subject = (resend ? "[#{t(:resend).upcase}] " : '') diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index cb04e3fa4c..6ad91ba140 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -6,6 +6,8 @@ class Enterprise < ActiveRecord::Base acts_as_gmappable :process_geocoding => false + after_create :send_creation_email + has_and_belongs_to_many :groups, class_name: 'EnterpriseGroup' has_many :producer_properties, foreign_key: 'producer_id' has_many :supplied_products, :class_name => 'Spree::Product', :foreign_key => 'supplier_id', :dependent => :destroy @@ -16,6 +18,7 @@ class Enterprise < ActiveRecord::Base has_many :enterprise_fees has_many :enterprise_roles, :dependent => :destroy has_many :users, through: :enterprise_roles + belongs_to :owner, class_name: 'Spree::User', foreign_key: :owner_id, inverse_of: :owned_enterprises has_and_belongs_to_many :payment_methods, join_table: 'distributors_payment_methods', class_name: 'Spree::PaymentMethod', foreign_key: 'distributor_id' has_many :distributor_shipping_methods, foreign_key: :distributor_id has_many :shipping_methods, through: :distributor_shipping_methods @@ -46,7 +49,10 @@ class Enterprise < ActiveRecord::Base validates :name, presence: true validates :type, presence: true, inclusion: {in: TYPES} validates :address, presence: true, associated: true + validates_presence_of :owner + validate :enforce_ownership_limit, if: lambda { owner_id_changed? } + before_validation :ensure_owner_is_manager, if: lambda { owner_id_changed? } before_validation :set_unused_address_fields after_validation :geocode_address @@ -279,6 +285,10 @@ class Enterprise < ActiveRecord::Base private + def send_creation_email + EnterpriseMailer.creation_confirmation(self).deliver + end + def strip_url(url) url.andand.sub /(https?:\/\/)?/, '' end @@ -290,4 +300,14 @@ class Enterprise < ActiveRecord::Base def geocode_address address.geocode if address.changed? end + + def ensure_owner_is_manager + users << owner unless users.include?(owner) || owner.admin? + end + + def enforce_ownership_limit + unless owner.can_own_more_enterprises? + errors.add(:owner, "^You are not permitted to own own any more enterprises (limit is #{owner.enterprise_limit}).") + end + end end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 9dbc8226f5..06a413b469 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -1,13 +1,16 @@ Spree.user_class.class_eval do has_many :enterprise_roles, :dependent => :destroy has_many :enterprises, through: :enterprise_roles + has_many :owned_enterprises, class_name: 'Enterprise', foreign_key: :owner_id, inverse_of: :owner has_one :cart accepts_nested_attributes_for :enterprise_roles, :allow_destroy => true - attr_accessible :enterprise_ids, :enterprise_roles_attributes + attr_accessible :enterprise_ids, :enterprise_roles_attributes, :enterprise_limit after_create :send_signup_confirmation + validate :limit_owned_enterprises + def build_enterprise_roles Enterprise.all.each do |enterprise| unless self.enterprise_roles.find_by_enterprise_id enterprise.id @@ -19,4 +22,16 @@ Spree.user_class.class_eval do def send_signup_confirmation Spree::UserMailer.signup_confirmation(self).deliver end + + def can_own_more_enterprises? + owned_enterprises(:reload).size < enterprise_limit + end + + private + + def limit_owned_enterprises + if owned_enterprises.size > enterprise_limit + errors.add(:owned_enterprises, "^The nominated user is not permitted to own own any more enterprises (limit is #{enterprise_limit}).") + end + end end diff --git a/app/overrides/add_supplier_to_admin_product.rb b/app/overrides/add_supplier_to_admin_product.rb deleted file mode 100644 index a608f4e282..0000000000 --- a/app/overrides/add_supplier_to_admin_product.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/admin/products/_form", - :insert_top => "[data-hook='admin_product_form_right']", - :partial => "spree/admin/products/supplier_form", - :name => "add_supplier_to_admin_product", - :original => '18bd94de3eb8bdf8b669932bf04fc59e2e85288b') \ No newline at end of file diff --git a/app/overrides/admin/enterprises/show/add_distributor_info.html.haml.deface b/app/overrides/admin/enterprises/show/add_distributor_info.html.haml.deface deleted file mode 100644 index 866d632e18..0000000000 --- a/app/overrides/admin/enterprises/show/add_distributor_info.html.haml.deface +++ /dev/null @@ -1,4 +0,0 @@ -/ insert_after "[data-hook='long_description']" -%tr{'data-hook' => 'distributor_info'} - %th Distributor Info: - %td= @enterprise.distributor_info.andand.html_safe \ No newline at end of file diff --git a/app/overrides/spree/admin/products/_form/add_supplier.html.haml.deface b/app/overrides/spree/admin/products/_form/add_supplier.html.haml.deface new file mode 100644 index 0000000000..1cc609bef4 --- /dev/null +++ b/app/overrides/spree/admin/products/_form/add_supplier.html.haml.deface @@ -0,0 +1,7 @@ +/ insert_top "[data-hook='admin_product_form_right']" + += f.field_container :supplier do + = f.label :supplier + %br + = f.collection_select(:supplier_id, @producers, :id, :name, {:include_blank => true}, {:class => "select2"}) + = f.error_message_on :supplier diff --git a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface index 334739a4c2..7c627eec4e 100644 --- a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface +++ b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface @@ -7,7 +7,7 @@ = f.field_container :supplier do = f.label :supplier_id, t(:supplier) %span.required * - = f.collection_select(:supplier_id, Enterprise.is_primary_producer.managed_by(spree_current_user).by_name, :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"}) + = f.collection_select(:supplier_id, @producers, :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"}) = f.error_message_on :supplier .six.columns.omega = f.field_container :name do diff --git a/app/overrides/spree/admin/users/_form/add_enterprise_limit_form_element.html.haml.deface b/app/overrides/spree/admin/users/_form/add_enterprise_limit_form_element.html.haml.deface new file mode 100644 index 0000000000..0b00900b5c --- /dev/null +++ b/app/overrides/spree/admin/users/_form/add_enterprise_limit_form_element.html.haml.deface @@ -0,0 +1,5 @@ +/ insert_bottom "div[data-hook='admin_user_form_fields'] div.alpha" + += f.field_container :enterprise_limit do + = f.label :enterprise_limit, t(:enterprise_limit) + = f.text_field :enterprise_limit, :class => 'fullwidth' \ No newline at end of file diff --git a/app/overrides/spree/admin/users/index/add_enterprise_limit_column.html.haml.deface b/app/overrides/spree/admin/users/index/add_enterprise_limit_column.html.haml.deface new file mode 100644 index 0000000000..d16e186be8 --- /dev/null +++ b/app/overrides/spree/admin/users/index/add_enterprise_limit_column.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_before "td[data-hook='admin_users_index_row_actions']" + +%td.user_enterprise_limit= user.enterprise_limit \ No newline at end of file diff --git a/app/overrides/spree/admin/users/index/add_enterprise_limit_column_header.html.haml.deface b/app/overrides/spree/admin/users/index/add_enterprise_limit_column_header.html.haml.deface new file mode 100644 index 0000000000..f2222ef012 --- /dev/null +++ b/app/overrides/spree/admin/users/index/add_enterprise_limit_column_header.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_before "th[data-hook='admin_users_index_header_actions']" + +%th= sort_link @search,:enterprise_limit, t(:enterprise_limit) \ No newline at end of file diff --git a/app/overrides/spree/admin/users/index/reconfigure_column_spacing.html.haml.deface b/app/overrides/spree/admin/users/index/reconfigure_column_spacing.html.haml.deface new file mode 100644 index 0000000000..d666e1b7c5 --- /dev/null +++ b/app/overrides/spree/admin/users/index/reconfigure_column_spacing.html.haml.deface @@ -0,0 +1,6 @@ +/ replace "table#listing_users colgroup" + +%colgroup + %col{ style: "width: 65%" } + %col{ style: "width: 20%" } + %col{ style: "width: 15%" } \ No newline at end of file diff --git a/app/overrides/spree/admin/users/index/replace_show_link_with_edit_link.html.haml.deface b/app/overrides/spree/admin/users/index/replace_show_link_with_edit_link.html.haml.deface new file mode 100644 index 0000000000..330e06ea9d --- /dev/null +++ b/app/overrides/spree/admin/users/index/replace_show_link_with_edit_link.html.haml.deface @@ -0,0 +1,3 @@ +/ replace "code[erb-loud]:contains('link_to user.email, object_url(user)')" + += link_to user.email, edit_object_url(user) \ No newline at end of file diff --git a/app/overrides/users_add_enterprise_management.rb b/app/overrides/users_add_enterprise_management.rb deleted file mode 100644 index 4ca1dd9e49..0000000000 --- a/app/overrides/users_add_enterprise_management.rb +++ /dev/null @@ -1,6 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/admin/users/_form", - :insert_after => "[data-hook='admin_user_form_fields']", - :partial => "spree/admin/users/enterprises_form", - :name => "add_enterprises_to_user" - ) - diff --git a/app/serializers/api/country_serializer.rb b/app/serializers/api/country_serializer.rb new file mode 100644 index 0000000000..6561692cf6 --- /dev/null +++ b/app/serializers/api/country_serializer.rb @@ -0,0 +1,5 @@ +class Api::CountrySerializer < ActiveModel::Serializer + attributes :id, :name, :states + + has_many :states, serializer: Api::StateSerializer +end \ No newline at end of file diff --git a/app/serializers/api/currency_config_serializer.rb b/app/serializers/api/currency_config_serializer.rb new file mode 100644 index 0000000000..ab17af8715 --- /dev/null +++ b/app/serializers/api/currency_config_serializer.rb @@ -0,0 +1,32 @@ +class Api::CurrencyConfigSerializer < ActiveModel::Serializer + attributes :currency, :display_currency, :symbol, :symbol_position, :hide_cents, :decimal_mark, :thousands_separator + + def currency + Spree::Config[:currency] + end + + def display_currency + Spree::Config[:display_currency] + end + + def symbol + ::Money.new(1, Spree::Config[:currency]).symbol + end + + def symbol_position + Spree::Config[:currency_symbol_position] + end + + def hide_cents + Spree::Config[:hide_cents] + end + + def decimal_mark + Spree::Config[:currency_decimal_mark] + end + + def thousands_separator + Spree::Config[:currency_thousands_separator] + end + +end diff --git a/app/serializers/api/payment_method_serializer.rb b/app/serializers/api/payment_method_serializer.rb index 6c2203083d..d62bc18154 100644 --- a/app/serializers/api/payment_method_serializer.rb +++ b/app/serializers/api/payment_method_serializer.rb @@ -1,3 +1,3 @@ class Api::PaymentMethodSerializer < ActiveModel::Serializer - attributes :name, :id, :method_type + attributes :name, :description, :id, :method_type end diff --git a/app/serializers/api/state_serializer.rb b/app/serializers/api/state_serializer.rb new file mode 100644 index 0000000000..bcf9221ec5 --- /dev/null +++ b/app/serializers/api/state_serializer.rb @@ -0,0 +1,3 @@ +class Api::StateSerializer < ActiveModel::Serializer + attributes :id, :name, :abbr +end \ No newline at end of file diff --git a/app/serializers/api/variant_serializer.rb b/app/serializers/api/variant_serializer.rb index e03f1e0ec8..5871d6def4 100644 --- a/app/serializers/api/variant_serializer.rb +++ b/app/serializers/api/variant_serializer.rb @@ -1,12 +1,12 @@ class Api::VariantSerializer < ActiveModel::Serializer attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display, - :on_demand, :price, :fees, :base_price + :on_demand, :price, :fees, :price_with_fees - def price + def price_with_fees object.price_with_fees(options[:current_distributor], options[:current_order_cycle]) end - def base_price + def price object.price end diff --git a/app/serializers/spree/api/variant_serializer.rb b/app/serializers/spree/api/variant_serializer.rb index f594a540ef..cd31196ca0 100644 --- a/app/serializers/spree/api/variant_serializer.rb +++ b/app/serializers/spree/api/variant_serializer.rb @@ -7,6 +7,7 @@ class Spree::Api::VariantSerializer < ActiveModel::Serializer end def price + # Decimals are passed to json as strings, we need to run parseFloat.toFixed(2) on the client side. object.price.nil? ? 0.to_f : object.price end -end \ No newline at end of file +end diff --git a/app/views/admin/enterprise_roles/index.html.haml b/app/views/admin/enterprise_roles/index.html.haml index 0881a8a50e..a6e0985a99 100644 --- a/app/views/admin/enterprise_roles/index.html.haml +++ b/app/views/admin/enterprise_roles/index.html.haml @@ -11,5 +11,5 @@ %table#enterprise-roles %tbody = render 'form' - %tr{"ng-repeat" => "enterprise_role in EnterpriseRoles.enterprise_roles | filter:query"} + %tr{"ng-repeat" => "enterprise_role in EnterpriseRoles.enterprise_roles | filter:query", id: "enterprise_role_{{enterprise_role.id}}"} = render 'enterprise_role' diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index dbf986c7aa..6f86c3e703 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -1,6 +1,3 @@ -- content_for :head do - = render 'shared/cms_elrte_head' - - content_for :page_actions do %li= button_link_to "Back to enterprises list", main_app.admin_enterprises_path, icon: 'icon-arrow-left' @@ -23,6 +20,16 @@ .eight.columns.omega = f.collection_select :group_ids, EnterpriseGroup.all, :id, :name, {}, class: "select2 fullwidth", multiple: true, placeholder: "Start typing to search available groups..." + - if spree_current_user.admin? + .row + .three.columns.alpha + =f.label :owner_id, 'Owner' + .with-tip{'data-powertip' => "The primary user responsible for this enterprise."} + %a What's this? + .eight.columns + - owner_email = @enterprise.andand.owner.andand.email || "" + = f.hidden_field :owner_id, class: "select2 fullwidth", 'ofn-user-autocomplete' => true, email: owner_email + .row .three.columns.alpha %label Enterprise Type(s) @@ -174,20 +181,8 @@ .row .alpha.three.columns = f.label :long_description, 'About Us' - %br - Tell us about yourself. This information appears on your public profile (under "About Us") .omega.eight.columns - = f.text_area :long_description, class: 'rich_text', placeholder: 'Tell us about yourself. This information appears on your public profile (under "About Us")' - .row - .alpha.three.columns - = f.label :distributor_info, 'How does your hub work?' - %br - %em (Hub only) - %br - Explain your distribution offer/s - this information appears on your public profile (under "How does it work?") - .omega.eight.columns - = f.text_area :distributor_info, class: 'rich_text', placeholder: 'Hub only: Explain your distribution offer/s - this is more detailed information that the user can access by clicking on "How does it work?"' - / TODO: editor breaks scrolling with arrow keys + = f.text_area :long_description, rows: 6, placeholder: 'Tell us about yourself. This information appears on your public profile (under "About Us")', class: 'fullwidth' %fieldset.eleven.columns.alpha.no-border-bottom %legend IMAGES .row diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index c8b3cce3d4..44008b3bd2 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -2,8 +2,9 @@ Enterprises - content_for :page_actions do - %li#new_product_link - = button_link_to "New Enterprise", main_app.new_admin_enterprise_path, :icon => 'icon-plus', :id => 'admin_new_enterprise_link' + - if spree_current_user.can_own_more_enterprises? + %li#new_product_link + = button_link_to "New Enterprise", main_app.new_admin_enterprise_path, :icon => 'icon-plus', :id => 'admin_new_enterprise_link' = render 'admin/shared/enterprises_sub_menu' @@ -11,16 +12,20 @@ %table#listing_enterprises.index %colgroup %col{style: "width: 25%;"}/ - %col{style: "width: 10%;"}/ + %col{style: "width: 15%;"}/ %col{style: "width: 5%;"}/ - %col{style: "width: 10%;"}/ - %col{style: "width: 20%;"}/ + - if spree_current_user.admin? + %col{style: "width: 12%;"}/ + %col{style: "width: 18%;"}/ + %col{style: "width: 25%;"}/ %thead %tr{"data-hook" => "enterprises_header"} %th Name %th Role %th Visible? - %th Type + - if spree_current_user.admin? + %th Type + %th Owner %th %tbody = f.fields_for :collection do |enterprise_form| @@ -34,7 +39,9 @@ = enterprise_form.check_box :is_distributor Hub %td= enterprise_form.check_box :visible - %td= enterprise_form.select :type, Enterprise::TYPES, {}, class: 'select2 fullwidth' + - if spree_current_user.admin? + %td= enterprise_form.select :type, Enterprise::TYPES, {}, class: 'select2 fullwidth' + %td= enterprise_form.select :owner_id, enterprise.users.map{ |e| [ e.email, e.id ] }, {}, class: "select2 fullwidth" %td{"data-hook" => "admin_users_index_row_actions"} = render 'actions', enterprise: enterprise - if @enterprises.empty? diff --git a/app/views/checkout/_payment.html.haml b/app/views/checkout/_payment.html.haml index b655659738..21dca1542c 100644 --- a/app/views/checkout/_payment.html.haml +++ b/app/views/checkout/_payment.html.haml @@ -17,6 +17,7 @@ %em %small {{ Checkout.paymentMethod().name }} + %small .small-4.medium-2.columns.text-right %span.accordion-up %em @@ -29,16 +30,20 @@ -# TODO render this in Angular instead of server-side -# The problem being how to render the partials - - current_order.available_payment_methods.each do |method| - .row - .small-12.columns - %label - = radio_button_tag "order[payments_attributes][][payment_method_id]", method.id, false, - required: true, - "ng-model" => "order.payment_method_id" - = method.name + .row + .small-6.columns + - current_order.available_payment_methods.each do |method| + .row + .small-12.columns + %label + = radio_button_tag "order[payments_attributes][][payment_method_id]", method.id, false, + required: true, + "ng-model" => "order.payment_method_id" + = method.name - .row{"ng-if" => "order.payment_method_id == #{method.id}"} - .small-12.columns - = render partial: "spree/checkout/payment/#{method.method_type}", :locals => { :payment_method => method } + .row{"ng-if" => "order.payment_method_id == #{method.id}"} + .small-12.columns + = render partial: "spree/checkout/payment/#{method.method_type}", :locals => { :payment_method => method } + .small-6.columns + %small {{ Checkout.paymentMethod().description }} diff --git a/app/views/checkout/_summary.html.haml b/app/views/checkout/_summary.html.haml index 0e03af7413..8609fd7a16 100644 --- a/app/views/checkout/_summary.html.haml +++ b/app/views/checkout/_summary.html.haml @@ -5,7 +5,7 @@ %table %tr %th Cart total - %td.cart-total.text-right= number_to_currency checkout_cart_total_with_adjustments(current_order) + %td.cart-total.text-right= spree_number_to_currency(checkout_cart_total_with_adjustments(current_order)) - checkout_adjustments_for_summary(current_order, exclude: [:shipping, :distribution]).each do |adjustment| %tr @@ -14,11 +14,11 @@ %tr %th Shipping - %td.shipping.text-right {{ Checkout.shippingPrice() | currency }} + %td.shipping.text-right {{ Checkout.shippingPrice() | localizeCurrency }} %tr %th Total - %td.total.text-right {{ Checkout.cartTotal() | currency }} + %td.total.text-right {{ Checkout.cartTotal() | localizeCurrency }} - if current_order.price_adjustment_totals.present? - current_order.price_adjustment_totals.each do |label, total| %tr diff --git a/app/views/enterprise_mailer/creation_confirmation.html.haml b/app/views/enterprise_mailer/creation_confirmation.html.haml new file mode 100644 index 0000000000..0df3bf06a0 --- /dev/null +++ b/app/views/enterprise_mailer/creation_confirmation.html.haml @@ -0,0 +1,9 @@ +%h1 + = @enterprise.name + " has been created" + +%h3 + Why not check it out on + %a{ href: "#{map_url}" } + = Spree::Config[:site_name] + "?" + +If you have any questions, please get in touch with us at: hello@openfoodnetwork.org diff --git a/app/views/home/_hubs.html.haml b/app/views/home/_hubs.html.haml index ede6750b83..ff90a45d48 100644 --- a/app/views/home/_hubs.html.haml +++ b/app/views/home/_hubs.html.haml @@ -14,7 +14,7 @@ / %i.ofn-i_020-search %input{type: :text, "ng-model" => "query", - placeholder: "Search by Name or Suburb...", + placeholder: "Search by name or suburb...", "ng-debounce" => "150", "ofn-disable-enter" => true} diff --git a/app/views/home/_skinny.html.haml b/app/views/home/_skinny.html.haml index 889c7ac331..292ece80a0 100644 --- a/app/views/home/_skinny.html.haml +++ b/app/views/home/_skinny.html.haml @@ -12,12 +12,16 @@ .columns.small-6.medium-3.large-4.text-right{"bo-if" => "hub.active"} %a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} %i.ofn-i_033-open-sign - %span.margin-top {{ hub.orders_close_at | sensible_timeframe }} + %span.margin-top{ bo: { if: "current()" } } + %em Shopping here + %span.margin-top{ bo: { if: "!current()" } } {{ hub.orders_close_at | sensible_timeframe }} .columns.small-6.medium-3.large-4.text-right{"bo-if" => "!hub.active"} %a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} %i.ofn-i_032-closed-sign - %span.margin-top Orders closed + %span.margin-top{ bo: { if: "current()" } } + %em Shopping here + %span.margin-top{ bo: { if: "!current()" } } Orders closed diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 07b0db8dfa..0c6ec608d4 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -29,6 +29,7 @@ = inject_json "railsFlash", "flash" = inject_taxons = inject_current_order + = inject_currency_config .off-canvas-wrap{offcanvas: true} .inner-wrap diff --git a/app/views/layouts/registration.html.haml b/app/views/layouts/registration.html.haml new file mode 100644 index 0000000000..8946a27de1 --- /dev/null +++ b/app/views/layouts/registration.html.haml @@ -0,0 +1,36 @@ +%html + %head + %meta{charset: 'utf-8'}/ + %meta{name: 'viewport', content: "width=device-width,initial-scale=1.0"}/ + + %title= content_for?(:title) ? yield(:title) : 'Welcome to Open Food Network' + - if Rails.env.production? + = favicon_link_tag + - else + = favicon_link_tag "/favicon-staging.ico" + %link{href: "https://fonts.googleapis.com/css?family=Open+Sans:400,700", rel: "stylesheet", type: "text/css"}/ + + = yield :scripts + %script{src: "//maps.googleapis.com/maps/api/js?libraries=places&sensor=false"} + = stylesheet_link_tag "darkswarm/all" + = javascript_include_tag "darkswarm/all" + + + = render "layouts/bugherd_script" + = csrf_meta_tags + + %body.off-canvas{"ng-app" => "Darkswarm", style: 'background-image: url("/assets/home/ofn_bg_1.jpg")' } + / [if lte IE 8] + = render partial: "shared/ie_warning" + = javascript_include_tag "iehack" + + = inject_json "user", "current_user" + + .off-canvas-wrap{offcanvas: true} + .inner-wrap + + %section{ role: "main" } + = yield + + #footer + %loading diff --git a/app/views/modals/_producer.html.haml b/app/views/modals/_producer.html.haml index 5d2d342f6d..51a45cfe5f 100644 --- a/app/views/modals/_producer.html.haml +++ b/app/views/modals/_producer.html.haml @@ -50,7 +50,7 @@ %a{"ng-href" => "http://instagram.com/{{enterprise.instagram}}", target: "_blank"} %i.ofn-i_043-instagram - %a.close-reveal-modal.outside{"ng-click" => "$close()"} + %a.close-reveal-modal{"ng-click" => "$close()"} %i.ofn-i_009-close diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml index a8512c9a22..f420b33386 100644 --- a/app/views/producers/_fat.html.haml +++ b/app/views/producers/_fat.html.haml @@ -1,13 +1,13 @@ .row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle()", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} .columns.small-12.medium-7.large-7.fat - / No long description available because it spits out HTML formatting producer.long_description - %div{"bo-if" => "producer.long_description"} + / Will add in long description available once clean up HTML formatting producer.long_description + %div{"bo-if" => "producer.description"} %label About us %img.right.show-for-medium-up{src: "{{ producer.logo }}" } %p.text-small - {{ producer.long_description }} - %div.show-for-medium-up{"bo-if" => "producer.long_description.length==0"} + {{ producer.description }} + %div.show-for-medium-up{"bo-if" => "producer.description.length==0"} %label   .columns.small-12.medium-5.large-5.fat diff --git a/app/views/producers/index.html.haml b/app/views/producers/index.html.haml index b8645f936a..76dc60a7e6 100644 --- a/app/views/producers/index.html.haml +++ b/app/views/producers/index.html.haml @@ -13,7 +13,7 @@ .small-12.columns %input.animate-show{type: :text, "ng-model" => "query", - placeholder: "Search by Producer or Suburb...", + placeholder: "Search by producer or suburb...", "ng-debounce" => "150", "ofn-disable-enter" => true} diff --git a/app/views/registration/authenticate.html.haml b/app/views/registration/authenticate.html.haml new file mode 100644 index 0000000000..2b65757a5d --- /dev/null +++ b/app/views/registration/authenticate.html.haml @@ -0,0 +1 @@ +%div{"ng-controller" => "AuthenticationCtrl"} \ No newline at end of file diff --git a/app/views/registration/index.html.haml b/app/views/registration/index.html.haml new file mode 100644 index 0000000000..de09d9494f --- /dev/null +++ b/app/views/registration/index.html.haml @@ -0,0 +1,4 @@ +=inject_spree_api_key +=inject_available_countries +=inject_enterprise_attributes +%div{ "ng-controller" => "RegistrationCtrl" } \ No newline at end of file diff --git a/app/views/shared/menu/_cart.html.haml b/app/views/shared/menu/_cart.html.haml index a61f80bf88..345365735b 100644 --- a/app/views/shared/menu/_cart.html.haml +++ b/app/views/shared/menu/_cart.html.haml @@ -22,20 +22,20 @@ %small {{line_item.quantity}} %i.ofn-i_009-close - {{ line_item.variant.price | currency }} + {{ line_item.variant.price_with_fees | localizeCurrency }} .columns.small-2 %small \= %strong - .right {{ line_item.variant.getPrice() | currency }} + .right {{ line_item.variant.totalPrice() | localizeCurrency }} %li.total-cart{"ng-show" => "Cart.line_items_present().length > 0"} .row .columns.small-6 %em Total: .columns.small-6.text-right - %strong {{ Cart.total() | currency }} + %strong {{ Cart.total() | localizeCurrency }} .text-right %a.button.primary.small{href: checkout_path, "ng-disabled" => "Cart.dirty"} Quick checkout diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 08a05595c5..f701813e9e 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -4,7 +4,9 @@ = render :partial => 'spree/admin/shared/order_sub_menu' -%div{ 'ng-app' => 'ofn.admin', 'ng-controller' => 'AdminOrderMgmtCtrl', 'ng-init' => "initialise('#{@spree_api_key}');loading=true;" } +=admin_inject_spree_api_key + +%div{ ng: { app: 'ofn.admin', controller: 'AdminOrderMgmtCtrl', init: 'initialise()' } } %div{ 'ng-show' => '!spree_api_key_ok' } {{ api_error_msg }} .filters{ :class => "sixteen columns alpha" } diff --git a/app/views/spree/admin/overview/_enterprises_header.html.haml b/app/views/spree/admin/overview/_enterprises_header.html.haml index d53828ca65..fcc7d269f2 100644 --- a/app/views/spree/admin/overview/_enterprises_header.html.haml +++ b/app/views/spree/admin/overview/_enterprises_header.html.haml @@ -1,7 +1,8 @@ %div.header.sixteen.columns.alpha{ :class => "#{@enterprises.count > 0 ? "" : "red"}"} %h3.thirteen.columns.alpha My Enterprises - if @enterprises.any? - %a.three.columns.omega.icon-plus.button.blue.white-bottom{ href: "#{main_app.new_admin_enterprise_path}" } - CREATE NEW + - if spree_current_user.can_own_more_enterprises? + %a.three.columns.omega.icon-plus.button.blue.white-bottom{ href: "#{main_app.new_admin_enterprise_path}" } + CREATE NEW - else %a.with-tip{ title: "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } What's this? diff --git a/app/views/spree/admin/products/_supplier_form.html.haml b/app/views/spree/admin/products/_supplier_form.html.haml deleted file mode 100644 index 3e5c01c2a3..0000000000 --- a/app/views/spree/admin/products/_supplier_form.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -= f.field_container :supplier do - = f.label :supplier - %br - = f.collection_select(:supplier_id, Enterprise.is_primary_producer.managed_by(spree_current_user).by_name, :id, :name, {:include_blank => true}, {:class => "select2"}) - = f.error_message_on :supplier diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml index fa322931cd..f230d1c331 100644 --- a/app/views/spree/admin/products/bulk_edit.html.haml +++ b/app/views/spree/admin/products/bulk_edit.html.haml @@ -1,7 +1,7 @@ = render 'spree/admin/products/bulk_edit/header' = render 'spree/admin/products/bulk_edit/data' -%div{ 'ng-app' => 'ofn.admin', 'ng-controller' => 'AdminProductEditCtrl', 'ng-init' => "initialise('#{@spree_api_key}');loading=true;" } +%div{ ng: { app: 'ofn.admin', controller: 'AdminProductEditCtrl', init: 'initialise()' } } = render 'spree/admin/products/bulk_edit/filters' %hr.sixteen.columns.alpha diff --git a/app/views/spree/admin/products/bulk_edit/_data.html.haml b/app/views/spree/admin/products/bulk_edit/_data.html.haml index 25d595bda1..8b727be3eb 100644 --- a/app/views/spree/admin/products/bulk_edit/_data.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_data.html.haml @@ -1,2 +1,3 @@ = admin_inject_producers = admin_inject_taxons += admin_inject_spree_api_key diff --git a/app/views/spree/admin/users/_enterprises_form.html.haml b/app/views/spree/admin/users/_enterprises_form.html.haml deleted file mode 100644 index 03cecd41d5..0000000000 --- a/app/views/spree/admin/users/_enterprises_form.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -%fieldset - %legend 'Manage Enterprises' - = f.field_container :enterprise_roles do - - f.object.build_enterprise_roles - %table - = f.fields_for :enterprise_roles do |enterprise_form| - %tr - %td - = hidden_field_tag "#{enterprise_form.object_name}[_destroy]", 1, :id => nil - = check_box_tag "#{enterprise_form.object_name}[_destroy]", 0, !enterprise_form.object.new_record? - %td - = label_tag "#{enterprise_form.object_name}[_destroy]", enterprise_form.object.enterprise.name - = enterprise_form.hidden_field :enterprise_id diff --git a/app/views/spree/order_mailer/confirm_email.text.erb b/app/views/spree/order_mailer/confirm_email.text.erb deleted file mode 100644 index 932eaebe4c..0000000000 --- a/app/views/spree/order_mailer/confirm_email.text.erb +++ /dev/null @@ -1,68 +0,0 @@ -Dear <%= @order.bill_address.firstname %>, - -Please review and retain the following order information for your records. - -============================================================ -Order Summary -============================================================ -Order for: <%= @order.bill_address.full_name %> -<% @order.line_items.each do |item| %> - <%= item.variant.sku %> <%= raw(item.variant.product.supplier.name) %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (QTY: <%=item.quantity%>) @ <%= item.single_money %> = <%= item.display_amount %> -<% end %> -============================================================ -Subtotal: <%= number_to_currency checkout_cart_total_with_adjustments(@order) %> -<% checkout_adjustments_for_summary(@order, exclude: [:distribution]).each do |adjustment| %> - <%= raw(adjustment.label) %> <%= adjustment.display_amount %> -<% end %> -Order Total: <%= @order.display_total %> - -<% if @order.payments.first.andand.payment_method.andand.name.andand.include? "EFT" %> -============================================================ -Payment Details -============================================================ -<%= @order.payments.first.andand.payment_method.andand.description.andand.html_safe %> - -<% end %> -<% if @order.shipping_method.andand.require_ship_address %> -============================================================ -Delivery Details -============================================================ -Your order will be delivered to: -<%= @order.ship_address.to_s %> - -<% if @order.order_cycle.andand.pickup_time_for(@order.distributor) %> -Delivery on: <%= @order.order_cycle.pickup_time_for(@order.distributor) %> - -<% end %> -<% if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) %> -Other delivery information: <%= @order.order_cycle.pickup_instructions_for(@order.distributor) %> - -<% end %> -<% else %> -============================================================ -Collection Details -============================================================ -<% if @order.shipping_method.andand.description %> -<%= @order.shipping_method.description.html_safe %> - -<% end %> -<% if @order.order_cycle.andand.pickup_time_for(@order.distributor) %> -Ready for collection: <%= @order.order_cycle.pickup_time_for(@order.distributor) %> - -<% end %> -<% if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) %> -Collection instructions: <%= @order.order_cycle.pickup_instructions_for(@order.distributor) %> - -<% end %> -<% end %> -<% if @order.special_instructions.present? %>Order notes: <%= @order.special_instructions %> - -<% end %> - -Thanks for your support. - -<%= @order.distributor.contact %>, -<%= @order.distributor.name %> -<%= @order.distributor.phone %> -<%= @order.distributor.email %> -<%= @order.distributor.website %> diff --git a/app/views/spree/order_mailer/confirm_email.text.haml b/app/views/spree/order_mailer/confirm_email.text.haml new file mode 100644 index 0000000000..1ebc1a89af --- /dev/null +++ b/app/views/spree/order_mailer/confirm_email.text.haml @@ -0,0 +1,60 @@ +Dear #{@order.bill_address.firstname}, + +Please review and retain the following order information for your records. +\ +============================================================ +Order Summary +============================================================ +Order for: #{@order.bill_address.full_name} +- @order.line_items.each do |item| + #{item.variant.sku} #{raw(item.variant.product.supplier.name)} #{raw(item.variant.product.name)} #{raw(item.variant.options_text)} (QTY: #{item.quantity}) @ #{item.single_money} = #{item.display_amount} +============================================================ +Subtotal: #{number_to_currency checkout_cart_total_with_adjustments(@order)} +- checkout_adjustments_for_summary(@order, exclude: [:distribution]).each do |adjustment| + #{raw(adjustment.label)} #{adjustment.display_amount} +Order Total: #{@order.display_total} +- if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" + \ + ============================================================ + Payment Details + ============================================================ + #{@order.payments.first.andand.payment_method.andand.description.andand.html_safe} + +- if @order.shipping_method.andand.require_ship_address + \ + ============================================================ + Delivery Details + ============================================================ + Your order will be delivered to: + #{@order.ship_address.to_s} + +- if @order.order_cycle.andand.pickup_time_for(@order.distributor) + Delivery on: #{@order.order_cycle.pickup_time_for(@order.distributor)} + +- if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + Other delivery information: #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + +- else + \ + ============================================================ + Collection Details + ============================================================ + - if @order.shipping_method.andand.description + #{@order.shipping_method.description.html_safe} + + - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + Ready for collection: #{@order.order_cycle.pickup_time_for(@order.distributor)} + + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor) + Collection instructions: #{@order.order_cycle.pickup_instructions_for(@order.distributor)} + + - if @order.special_instructions.present? + notes: #{@order.special_instructions} + +Thanks for your support. + +#{@order.distributor.contact}, += @order.distributor.name += @order.distributor.phone += @order.distributor.email += @order.distributor.website diff --git a/app/views/spree/orders/_form.html.haml b/app/views/spree/orders/_form.html.haml index 214823d4cf..953ea83e18 100644 --- a/app/views/spree/orders/_form.html.haml +++ b/app/views/spree/orders/_form.html.haml @@ -24,7 +24,7 @@ %td Product \: - %span.order-total.item-total= number_to_currency @order.item_total + %span.order-total.item-total= spree_number_to_currency(@order.item_total) %td Distribution \: diff --git a/app/views/spree/shared/_products.html.haml b/app/views/spree/shared/_products.html.haml index bdb8b87600..8ea2743642 100644 --- a/app/views/spree/shared/_products.html.haml +++ b/app/views/spree/shared/_products.html.haml @@ -15,7 +15,7 @@ .product-image = link_to small_image(product, :itemprop => "image"), product, :itemprop => 'url' = link_to truncate(product.name, :length => 50), product, :class => 'info', :itemprop => "name", :title => product.name - %span.price.selling{:itemprop => "price"}= number_to_currency product.price + %span.price.selling{:itemprop => "price"}= spree_number_to_currency(product.price) - if paginated_products.respond_to?(:num_pages) - params.delete(:search) diff --git a/config/routes.rb b/config/routes.rb index 5230e798cc..f95b0c07df 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,6 +5,10 @@ Openfoodnetwork::Application.routes.draw do get "/map", to: "map#index", as: :map + get "/register", to: "registration#index", as: :registration + get "/register/store", to: "registration#store", as: :store_registration + get "/register/auth", to: "registration#authenticate", as: :registration_auth + resource :shop, controller: "shop" do get :products post :order_cycle diff --git a/db/migrate/20140828023619_add_owner_to_enterprise.rb b/db/migrate/20140828023619_add_owner_to_enterprise.rb new file mode 100644 index 0000000000..8491e9c599 --- /dev/null +++ b/db/migrate/20140828023619_add_owner_to_enterprise.rb @@ -0,0 +1,21 @@ +class AddOwnerToEnterprise < ActiveRecord::Migration + def up + add_column :enterprises, :owner_id, :integer + add_index :enterprises, :owner_id + + Enterprise.all.each do |e| + owner = e.users.find{ |u| !u.admin? } + admin_owner = e.users.find &:admin? + any_admin = Spree::User.admin.first + any_user = Spree::User.first + e.update_column :owner_id, (owner || admin_owner || any_admin || any_user ) + end + + add_foreign_key :enterprises, :spree_users, column: :owner_id + change_column :enterprises, :owner_id, :integer, null: false + end + + def down + remove_column :enterprises, :owner_id + end +end diff --git a/db/migrate/20140904003026_add_enterprise_limit_to_spree_users.rb b/db/migrate/20140904003026_add_enterprise_limit_to_spree_users.rb new file mode 100644 index 0000000000..b76a3da2d7 --- /dev/null +++ b/db/migrate/20140904003026_add_enterprise_limit_to_spree_users.rb @@ -0,0 +1,18 @@ +class AddEnterpriseLimitToSpreeUsers < ActiveRecord::Migration + def up + add_column :spree_users, :enterprise_limit, :integer, default: 1, null: false + + Spree::User.all.each do |u| + e_count = u.owned_enterprises.length + if u.admin? || e_count > 1 + e_limit = 100 + e_limit = 1000 if u.admin? + u.update_column :enterprise_limit, e_limit + end + end + end + + def down + remove_column :spree_users, :enterprise_limit + end +end diff --git a/db/schema.rb b/db/schema.rb index 462e89c952..73f10210a7 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 => 20140826043521) do +ActiveRecord::Schema.define(:version => 20140904003026) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -265,9 +265,11 @@ ActiveRecord::Schema.define(:version => 20140826043521) do t.string "instagram" t.string "linkedin" t.string "type", :default => "profile", :null => false + t.integer "owner_id", :null => false end add_index "enterprises", ["address_id"], :name => "index_enterprises_on_address_id" + add_index "enterprises", ["owner_id"], :name => "index_enterprises_on_owner_id" create_table "exchange_fees", :force => true do |t| t.integer "exchange_id" @@ -563,9 +565,9 @@ ActiveRecord::Schema.define(:version => 20140826043521) do t.string "email" t.text "special_instructions" t.integer "distributor_id" + t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" - t.integer "order_cycle_id" t.integer "cart_id" end @@ -969,6 +971,7 @@ ActiveRecord::Schema.define(:version => 20140826043521) do t.string "spree_api_key", :limit => 48 t.datetime "reset_password_sent_at" t.string "api_key", :limit => 40 + t.integer "enterprise_limit", :default => 1, :null => false end add_index "spree_users", ["email"], :name => "email_idx_unique", :unique => true @@ -1069,6 +1072,7 @@ ActiveRecord::Schema.define(:version => 20140826043521) do add_foreign_key "enterprise_roles", "spree_users", name: "enterprise_roles_user_id_fk", column: "user_id" add_foreign_key "enterprises", "spree_addresses", name: "enterprises_address_id_fk", column: "address_id" + add_foreign_key "enterprises", "spree_users", name: "enterprises_owner_id_fk", column: "owner_id" add_foreign_key "exchange_fees", "enterprise_fees", name: "exchange_fees_enterprise_fee_id_fk" add_foreign_key "exchange_fees", "exchanges", name: "exchange_fees_exchange_id_fk" diff --git a/db/suburb_seeds.rb b/db/suburb_seeds.rb index ea3ee531db..cd07f46ed3 100644 --- a/db/suburb_seeds.rb +++ b/db/suburb_seeds.rb @@ -15762,8 +15762,8 @@ module SuburbSeeder ($$6770$$,$$STURT CREEK$$,#{state_id_wa},-18.224393,127.66743), ($$6770$$,$$TANAMI$$,#{state_id_wa},-18.224393,127.66743), ($$6798$$,$$CHRISTMAS ISLAND$$,#{state_id_wa},-10.487053,105.64067), - ($$6799$$,$$HOME ISLAND COCOS ($$KEELING) ISLANDS$$,#{state_id_wa},-12.169719,96.83152), - ($$6799$$,$$WEST ISLAND COCOS ($$KEELING) ISLANDS$$,#{state_id_wa},-12.169719,96.83152), + ($$6799$$,$$HOME ISLAND COCOS (KEELING) ISLANDS$$,#{state_id_wa},-12.169719,96.83152), + ($$6799$$,$$WEST ISLAND COCOS (KEELING) ISLANDS$$,#{state_id_wa},-12.169719,96.83152), ($$6800$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), ($$6803$$,$$NORTHBRIDGE$$,#{state_id_wa},0.0,0.0), ($$6809$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), diff --git a/lib/open_food_network/spree_api_key_loader.rb b/lib/open_food_network/spree_api_key_loader.rb new file mode 100644 index 0000000000..36fa4b9961 --- /dev/null +++ b/lib/open_food_network/spree_api_key_loader.rb @@ -0,0 +1,12 @@ +module OpenFoodNetwork + module SpreeApiKeyLoader + def load_spree_api_key + if spree_current_user + spree_current_user.generate_spree_api_key! unless spree_current_user.spree_api_key + @spree_api_key = spree_current_user.spree_api_key + else + @spree_api_key = nil + end + end + end +end \ No newline at end of file diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 14faf26287..44421aad8e 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -2,7 +2,12 @@ require 'spec_helper' module Admin describe EnterprisesController do - let(:distributor) { create(:distributor_enterprise) } + let(:distributor_owner) do + user = create(:user) + user.spree_roles = [] + user + end + let(:distributor) { create(:distributor_enterprise, owner: distributor_owner ) } let(:user) do user = create(:user) user.spree_roles = [] @@ -22,6 +27,7 @@ module Admin it "grants management permission if the current user is an enterprise user" do controller.stub spree_current_user: user + enterprise_params[:enterprise][:owner_id] = user spree_put :create, enterprise_params enterprise = Enterprise.find_by_name 'zzz' @@ -30,11 +36,50 @@ module Admin it "does not grant management permission to admins" do controller.stub spree_current_user: admin_user + enterprise_params[:enterprise][:owner_id] = admin_user spree_put :create, enterprise_params enterprise = Enterprise.find_by_name 'zzz' admin_user.enterprise_roles.where(enterprise_id: enterprise).should be_empty end + + it "it overrides the owner_id submitted by the user unless current_user is super admin" do + controller.stub spree_current_user: user + enterprise_params[:enterprise][:owner_id] = admin_user + + spree_put :create, enterprise_params + enterprise = Enterprise.find_by_name 'zzz' + user.enterprise_roles.where(enterprise_id: enterprise).first.should be + end + end + + describe "updating an enterprise" do + it "allows current owner to change ownership" do + controller.stub spree_current_user: distributor_owner + update_params = { id: distributor, enterprise: { owner_id: user } } + spree_post :update, update_params + + distributor.reload + expect(distributor.owner).to eq user + end + + it "allows super admin to change ownership" do + controller.stub spree_current_user: admin_user + update_params = { id: distributor, enterprise: { owner_id: user } } + spree_post :update, update_params + + distributor.reload + expect(distributor.owner).to eq user + end + + it "does not allow managers to change ownership" do + controller.stub spree_current_user: user + update_params = { id: distributor, enterprise: { owner_id: user } } + spree_post :update, update_params + + distributor.reload + expect(distributor.owner).to eq distributor_owner + end end describe "updating an enterprise" do diff --git a/spec/controllers/registration_controller_spec.rb b/spec/controllers/registration_controller_spec.rb new file mode 100644 index 0000000000..49efc005f6 --- /dev/null +++ b/spec/controllers/registration_controller_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe RegistrationController do + describe "redirecting when user not logged in" do + it "index" do + get :index + response.should redirect_to registration_auth_path(anchor: "signup?after_login=/register") + end + + it "store" do + get :store + response.should redirect_to registration_auth_path(anchor: "signup?after_login=/register/store") + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 849d38abf3..d605b5b759 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -82,6 +82,7 @@ FactoryGirl.define do end factory :enterprise, :class => Enterprise do + owner { FactoryGirl.create :user } sequence(:name) { |n| "Enterprise #{n}" } type 'full' description 'enterprise' diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index fe18f1a9bf..5db9eee86d 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -10,7 +10,7 @@ feature %q{ after { Warden.test_reset! } stub_authorization! - context "listing orders" do + pending "listing orders" do before :each do admin_user = quick_login_as_admin end @@ -92,7 +92,7 @@ feature %q{ end end - context "altering line item properties" do + pending "altering line item properties" do before :each do admin_user = quick_login_as_admin end @@ -140,7 +140,7 @@ feature %q{ end end - context "using page controls" do + pending "using page controls" do before :each do admin_user = quick_login_as_admin end @@ -562,7 +562,7 @@ feature %q{ end end - context "as an enterprise manager" do + pending "as an enterprise manager" do let(:s1) { create(:supplier_enterprise, name: 'First Supplier') } let(:d1) { create(:distributor_enterprise, name: 'First Distributor') } let(:d2) { create(:distributor_enterprise, name: 'Another Distributor') } diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index 9f530f860c..8bb54cfca8 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -6,7 +6,7 @@ feature %q{ } , js: true do include AuthenticationWorkflow include WebHelper - + describe "listing products" do before :each do login_to_admin_section @@ -69,7 +69,7 @@ feature %q{ expect(page).to have_field "price", with: "44.0" expect(page).to have_no_field "price", with: "66.0", visible: true end - + it "displays an on hand count input for each product (ie. for master variant) if no regular variants exist" do p1 = FactoryGirl.create(:product) p2 = FactoryGirl.create(:product) @@ -84,7 +84,7 @@ feature %q{ expect(page).to have_field "on_hand", with: "15" expect(page).to have_field "on_hand", with: "12" end - + it "displays an on hand count in a span for each product (ie. for master variant) if other variants exist" do p1 = FactoryGirl.create(:product) p2 = FactoryGirl.create(:product) @@ -142,7 +142,7 @@ feature %q{ expect(page).to have_field "variant_unit_name", with: "packet" end end - + describe "listing variants" do before :each do login_to_admin_section @@ -174,8 +174,8 @@ feature %q{ expect(page).to have_field "variant_on_hand", with: "15" expect(page).to have_field "variant_on_hand", with: "6" end - - + + it "displays a price input (for each variant) for each product" do p1 = FactoryGirl.create(:product, price: 2.0) v1 = FactoryGirl.create(:variant, product: p1, is_master: false, price: 12.75) @@ -295,7 +295,7 @@ feature %q{ login_to_admin_section visit '/admin/products/bulk_edit' - + first("div#columns_dropdown", :text => "COLUMNS").click first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click first("div#columns_dropdown div.menu div.menu_item", text: "Category").click @@ -305,14 +305,13 @@ feature %q{ expect(page).to have_select "producer", selected: s1.name expect(page).to have_field "available_on", with: p.available_on.strftime("%F %T") expect(page).to have_field "price", with: "10.0" - save_screenshot '/Users/rob/Desktop/ss.png' expect(page).to have_selector "div#s2id_p#{p.id}_category a.select2-choice" expect(page).to have_select "variant_unit_with_scale", selected: "Volume (L)" expect(page).to have_field "on_hand", with: "6" fill_in "product_name", with: "Big Bag Of Potatoes" select s2.name, :from => 'producer' - fill_in "available_on", with: (Date.today-3).strftime("%F %T") + fill_in "available_on", with: (3.days.ago.beginning_of_day).strftime("%F %T") fill_in "price", with: "20" select "Weight (kg)", from: "variant_unit_with_scale" select2_select t1.name, from: "p#{p.id}_category" @@ -334,7 +333,7 @@ feature %q{ expect(p.on_hand).to eq 18 expect(p.primary_taxon).to eq t1 end - + scenario "updating a product with a variant unit of 'items'" do p = FactoryGirl.create(:product, variant_unit: 'weight', variant_unit_scale: 1000) @@ -590,7 +589,7 @@ feature %q{ within "tr#v_#{v1.id}" do first("a.delete-variant").click end - + expect(page).to have_selector "a.delete-variant", :count => 2 visit '/admin/products/bulk_edit' @@ -673,7 +672,7 @@ feature %q{ login_to_admin_section visit '/admin/products/bulk_edit' - + first("div#columns_dropdown", :text => "COLUMNS").click first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click @@ -745,8 +744,6 @@ feature %q{ permissions_list: [:manage_products]) end - use_short_wait - before do @enterprise_user = create_enterprise_user @enterprise_user.enterprise_roles.build(enterprise: supplier_managed1).save @@ -779,6 +776,28 @@ feature %q{ expect(page).to have_field 'product_name', with: product_supplied_inactive.name end + it "allows me to create a product" do + taxon = create(:taxon, name: 'Fruit') + + visit '/admin/products/bulk_edit' + + find("a", text: "NEW PRODUCT").click + expect(page).to have_content 'NEW PRODUCT' + expect(page).to have_select 'product_supplier_id', with_options: [supplier_managed1.name, supplier_managed2.name, supplier_permitted.name] + + within 'fieldset#new_product' do + fill_in 'product_name', with: 'Big Bag Of Apples' + select supplier_permitted.name, from: 'product_supplier_id' + fill_in 'product_price', with: '10.00' + select taxon.name, from: 'product_primary_taxon_id' + end + click_button 'Create' + + expect(URI.parse(current_url).path).to eq '/admin/products/bulk_edit' + expect(flash_message).to eq 'Product "Big Bag Of Apples" has been successfully created!' + expect(page).to have_field "product_name", with: 'Big Bag Of Apples' + end + it "allows me to update a product" do p = product_supplied_permitted @@ -795,7 +814,7 @@ feature %q{ fill_in "product_name", with: "Big Bag Of Potatoes" select(supplier_managed2.name, :from => 'producer') - fill_in "available_on", with: (Date.today-3).strftime("%F %T") + fill_in "available_on", with: (3.days.ago.beginning_of_day).strftime("%F %T") fill_in "price", with: "20" select "Weight (kg)", from: "variant_unit_with_scale" fill_in "on_hand", with: "18" diff --git a/spec/features/admin/enterprise_roles_spec.rb b/spec/features/admin/enterprise_roles_spec.rb index 0e44c9cfe3..7256b4e03c 100644 --- a/spec/features/admin/enterprise_roles_spec.rb +++ b/spec/features/admin/enterprise_roles_spec.rb @@ -71,7 +71,9 @@ feature %q{ visit admin_enterprise_roles_path page.should have_relationship u, e - first("a.delete-enterprise-role").click + within("#enterprise_role_#{er.id}") do + find("a.delete-enterprise-role").click + end page.should_not have_relationship u, e EnterpriseRole.where(id: er.id).should be_empty diff --git a/spec/features/admin/enterprise_user_spec.rb b/spec/features/admin/enterprise_user_spec.rb index ed46a1e0c1..77378f6cbd 100644 --- a/spec/features/admin/enterprise_user_spec.rb +++ b/spec/features/admin/enterprise_user_spec.rb @@ -17,42 +17,18 @@ feature %q{ let(:distributor_profile) { create(:distributor_enterprise, name: 'Distributor profile', type: 'profile') } describe "creating an enterprise user" do - context "with no enterprises managed" do - it "assigns an enterprise to a user" do + context "with a limitted number of owned enterprises" do + scenario "setting the enterprise ownership limit" do + user.enterprise_limit.should == 1 login_to_admin_section click_link 'Users' click_link user.email - click_link 'Edit' - check supplier2.name + fill_in "user_enterprise_limit", with: 2 click_button 'Update' - user.enterprises.count.should == 1 - user.enterprises.first.name.should == supplier2.name - end - end - - context "with existing enterprises managed" do - before do - user.enterprise_roles.create!(enterprise: supplier1) - user.enterprise_roles.create!(enterprise: distributor1) - end - - it "can remove and add enterprise management for a user" do - login_to_admin_section - - click_link 'Users' - click_link user.email - click_link 'Edit' - - uncheck distributor1.name # remove - check distributor2.name # add - - click_button 'Update' - - user.enterprises.count.should == 2 - user.enterprises.should include supplier1 - user.enterprises.should include distributor2 + user.reload + expect(user.enterprise_limit).to eq 2 end end end diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index a824716d95..abaab5cd2d 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -38,6 +38,9 @@ feature %q{ scenario "editing enterprises in bulk" do s = create(:supplier_enterprise) d = create(:distributor_enterprise, type: 'profile') + d_manager = create_enterprise_user + d_manager.enterprise_roles.build(enterprise: d).save + expect(d.owner).to_not eq d_manager login_to_admin_section click_link 'Enterprises' @@ -46,12 +49,14 @@ feature %q{ expect(page).to have_checked_field "enterprise_set_collection_attributes_0_visible" uncheck "enterprise_set_collection_attributes_0_visible" select 'full', from: "enterprise_set_collection_attributes_0_type" + select d_manager.email, from: 'enterprise_set_collection_attributes_0_owner_id' end click_button "Update" flash_message.should == 'Enterprises updated successfully' distributor = Enterprise.find(d.id) expect(distributor.visible).to eq false expect(distributor.type).to eq 'full' + expect(distributor.owner).to eq d_manager end scenario "viewing an enterprise" do @@ -64,35 +69,31 @@ feature %q{ page.should have_content e.name end - scenario "creating a new enterprise" do + scenario "creating a new enterprise", js:true do eg1 = create(:enterprise_group, name: 'eg1') eg2 = create(:enterprise_group, name: 'eg2') payment_method = create(:payment_method) shipping_method = create(:shipping_method) enterprise_fee = create(:enterprise_fee) - login_to_admin_section - - click_link 'Enterprises' + # Navigating + admin = quick_login_as_admin + visit '/admin/enterprises' click_link 'New Enterprise' - fill_in 'enterprise_name', :with => 'Eaterprises' - choose 'Full' - fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' - fill_in 'enterprise_long_description', :with => 'Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.' - fill_in 'enterprise_distributor_info', :with => 'Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.' - + # Checking shipping and payment method sidebars work uncheck 'enterprise_is_primary_producer' check 'enterprise_is_distributor' - - select eg1.name, from: 'enterprise_group_ids' - page.should_not have_checked_field "enterprise_payment_method_ids_#{payment_method.id}" page.should_not have_checked_field "enterprise_shipping_method_ids_#{shipping_method.id}" + # Filling in details + fill_in 'enterprise_name', :with => 'Eaterprises' + select2_search admin.email, from: 'Owner' + choose 'Full' check "enterprise_payment_method_ids_#{payment_method.id}" check "enterprise_shipping_method_ids_#{shipping_method.id}" - + select2_search eg1.name, from: 'Groups' fill_in 'enterprise_contact', :with => 'Kirsten or Ren' fill_in 'enterprise_phone', :with => '0413 897 321' fill_in 'enterprise_email', :with => 'info@eaterprises.com.au' @@ -106,14 +107,16 @@ feature %q{ fill_in 'enterprise_address_attributes_address1', :with => '35 Ballantyne St' fill_in 'enterprise_address_attributes_city', :with => 'Thornbury' fill_in 'enterprise_address_attributes_zipcode', :with => '3072' - select('Australia', :from => 'enterprise_address_attributes_country_id') - select('Victoria', :from => 'enterprise_address_attributes_state_id') + select2_search 'Australia', :from => 'Country' + select2_search 'Victoria', :from => 'State' + fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' + fill_in 'enterprise_long_description', :with => 'Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.' click_button 'Create' flash_message.should == 'Enterprise "Eaterprises" has been successfully created!' end - scenario "editing an existing enterprise" do + scenario "editing an existing enterprise", js: true do @enterprise = create(:enterprise) e2 = create(:enterprise) eg1 = create(:enterprise_group, name: 'eg1') @@ -121,14 +124,18 @@ feature %q{ payment_method = create(:payment_method, distributors: [e2]) shipping_method = create(:shipping_method, distributors: [e2]) enterprise_fee = create(:enterprise_fee, enterprise: @enterprise ) + user = create(:user) - login_to_admin_section + admin = quick_login_as_admin - click_link 'Enterprises' - all("a", text:'Edit Profile').first.click + visit '/admin/enterprises' + within "tr.enterprise-#{@enterprise.id}" do + all("a", text: 'Edit Profile').first.click + end fill_in 'enterprise_name', :with => 'Eaterprises' choose 'Single' + select2_search user.email, from: 'Owner' fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' fill_in 'enterprise_long_description', :with => 'Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.' @@ -143,7 +150,7 @@ feature %q{ page.should have_selector "#shipping_methods" page.should have_selector "#enterprise_fees" - select eg1.name, from: 'enterprise_group_ids' + select2_search eg1.name, from: 'Groups' page.should_not have_checked_field "enterprise_payment_method_ids_#{payment_method.id}" page.should_not have_checked_field "enterprise_shipping_method_ids_#{shipping_method.id}" @@ -162,13 +169,15 @@ feature %q{ fill_in 'enterprise_address_attributes_address1', :with => '35 Ballantyne St' fill_in 'enterprise_address_attributes_city', :with => 'Thornbury' fill_in 'enterprise_address_attributes_zipcode', :with => '3072' - select('Australia', :from => 'enterprise_address_attributes_country_id') - select('Victoria', :from => 'enterprise_address_attributes_state_id') + select2_search 'Australia', :from => 'Country' + select2_search 'Victoria', :from => 'State' click_button 'Update' flash_message.should == 'Enterprise "Eaterprises" has been successfully updated!' page.should have_field 'enterprise_name', :with => 'Eaterprises' + @enterprise.reload + expect(@enterprise.owner).to eq user page.should have_checked_field "enterprise_payment_method_ids_#{payment_method.id}" page.should have_checked_field "enterprise_shipping_method_ids_#{shipping_method.id}" @@ -248,56 +257,91 @@ feature %q{ let(:supplier2) { create(:supplier_enterprise, name: 'Another Supplier') } let(:distributor1) { create(:distributor_enterprise, name: 'First Distributor') } let(:distributor2) { create(:distributor_enterprise, name: 'Another Distributor') } + let(:enterprise_user) { create_enterprise_user } before(:each) do - @new_user = create_enterprise_user - @new_user.enterprise_roles.build(enterprise: supplier1).save - @new_user.enterprise_roles.build(enterprise: distributor1).save + enterprise_user.enterprise_roles.build(enterprise: supplier1).save + enterprise_user.enterprise_roles.build(enterprise: distributor1).save - login_to_admin_as @new_user + login_to_admin_as enterprise_user end - scenario "can view enterprises I have permission to" do - oc_user_coordinating = create(:simple_order_cycle, { coordinator: supplier1, name: 'Order Cycle 1' } ) - oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier2, name: 'Order Cycle 2' } ) + context "listing enterprises" do + scenario "displays enterprises I have permission to manage" do + oc_user_coordinating = create(:simple_order_cycle, { coordinator: supplier1, name: 'Order Cycle 1' } ) + oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier2, name: 'Order Cycle 2' } ) - click_link "Enterprises" + click_link "Enterprises" - within("tr.enterprise-#{distributor1.id}") do - expect(page).to have_content distributor1.name - expect(page).to have_checked_field "enterprise_set_collection_attributes_0_is_distributor" - expect(page).to have_unchecked_field "enterprise_set_collection_attributes_0_is_primary_producer" - expect(page).to have_select "enterprise_set_collection_attributes_0_type" + within("tr.enterprise-#{distributor1.id}") do + expect(page).to have_content distributor1.name + expect(page).to have_checked_field "enterprise_set_collection_attributes_0_is_distributor" + expect(page).to have_unchecked_field "enterprise_set_collection_attributes_0_is_primary_producer" + expect(page).to_not have_select "enterprise_set_collection_attributes_0_type" + end + + within("tr.enterprise-#{supplier1.id}") do + expect(page).to have_content supplier1.name + expect(page).to have_unchecked_field "enterprise_set_collection_attributes_1_is_distributor" + expect(page).to have_checked_field "enterprise_set_collection_attributes_1_is_primary_producer" + expect(page).to_not have_select "enterprise_set_collection_attributes_1_type" + end + + expect(page).to_not have_content "supplier2.name" + expect(page).to_not have_content "distributor2.name" + + expect(find("#content-header")).to have_link "New Enterprise" end - within("tr.enterprise-#{supplier1.id}") do - expect(page).to have_content supplier1.name - expect(page).to have_unchecked_field "enterprise_set_collection_attributes_1_is_distributor" - expect(page).to have_checked_field "enterprise_set_collection_attributes_1_is_primary_producer" - expect(page).to have_select "enterprise_set_collection_attributes_1_type" - end + context "when I have reached my enterprise ownership limit" do + it "does not display the link to create a new enterprise" do + enterprise_user.owned_enterprises.push [supplier1] - expect(page).to_not have_content "supplier2.name" - expect(page).to_not have_content "distributor2.name" + click_link "Enterprises" + + page.should have_content supplier1.name + page.should have_content distributor1.name + expect(find("#content-header")).to_not have_link "New Enterprise" + end + end end - scenario "creating an enterprise" do - # When I create an enterprise - click_link 'Enterprises' - click_link 'New Enterprise' - fill_in 'enterprise_name', with: 'zzz' - fill_in 'enterprise_address_attributes_address1', with: 'z' - fill_in 'enterprise_address_attributes_city', with: 'z' - fill_in 'enterprise_address_attributes_zipcode', with: 'z' - click_button 'Create' + context "creating an enterprise" do + before do + # When I create an enterprise + click_link 'Enterprises' + click_link 'New Enterprise' + fill_in 'enterprise_name', with: 'zzz' + fill_in 'enterprise_address_attributes_address1', with: 'z' + fill_in 'enterprise_address_attributes_city', with: 'z' + fill_in 'enterprise_address_attributes_zipcode', with: 'z' + end - # Then it should be created - page.should have_content 'Enterprise "zzz" has been successfully created!' - enterprise = Enterprise.last - enterprise.name.should == 'zzz' + scenario "without violating rules" do + click_button 'Create' - # And I should be managing it - Enterprise.managed_by(@new_user).should include enterprise + # Then it should be created + page.should have_content 'Enterprise "zzz" has been successfully created!' + enterprise = Enterprise.last + enterprise.name.should == 'zzz' + + # And I should be managing it + Enterprise.managed_by(enterprise_user).should include enterprise + end + + context "overstepping my owned enterprises limit" do + before do + create(:enterprise, owner: enterprise_user) + end + + it "shows me an error message" do + click_button 'Create' + + # Then it should show me an error + expect(page).to_not have_content 'Enterprise "zzz" has been successfully created!' + expect(page).to have_content "You are not permitted to own own any more enterprises (limit is 1)." + end + end end scenario "editing enterprises I have permission to" do diff --git a/spec/features/admin/orders_spec.rb b/spec/features/admin/orders_spec.rb index dd733a7009..d45778635a 100644 --- a/spec/features/admin/orders_spec.rb +++ b/spec/features/admin/orders_spec.rb @@ -129,26 +129,40 @@ feature %q{ visit '/admin/orders' click_link 'New Order' - page.should have_content 'ADD PRODUCT' + expect(page).to have_content 'ADD PRODUCT' targetted_select2_search product.name, from: '#add_variant_id', dropdown_css: '.select2-drop' + + puts "c1: " + coordinator1.id.to_s + " "+ coordinator1.name + puts "c2: " + coordinator2.id.to_s + " "+ coordinator2.name + puts "s1: " + supplier1.id.to_s + " "+ supplier1.name + puts "s2: " + supplier2.id.to_s + " "+ supplier2.name + puts "d1: " + distributor1.id.to_s + " "+ distributor1.name + puts "d2: " + distributor2.id.to_s + " "+ distributor2.name + order_cycle1.distributors.each do |distributor| + puts "oc1d: " + distributor.id.to_s + " "+ distributor.name + end + Enterprise.is_distributor.managed_by(@enterprise_user).each do |distributor| + puts "eud: " + distributor.id.to_s + " "+ distributor.name + end + click_link 'Add' page.has_selector? "table.index tbody[data-hook='admin_order_form_line_items'] tr" # Wait for JS - page.should have_selector 'td', text: product.name + expect(page).to have_selector 'td', text: product.name - page.should have_select 'order_distributor_id', with_options: [distributor1.name] - page.should have_no_select 'order_distributor_id', with_options: [distributor2.name] + expect(page).to have_select 'order_distributor_id', with_options: [distributor1.name] + expect(page).to_not have_select 'order_distributor_id', with_options: [distributor2.name] - page.should have_select 'order_order_cycle_id', with_options: [order_cycle1.name] - page.should have_no_select 'order_order_cycle_id', with_options: [order_cycle2.name] + expect(page).to have_select 'order_order_cycle_id', with_options: [order_cycle1.name] + expect(page).to_not have_select 'order_order_cycle_id', with_options: [order_cycle2.name] select distributor1.name, from: 'order_distributor_id' select order_cycle1.name, from: 'order_order_cycle_id' click_button 'Update' - page.should have_selector 'h1', text: 'Customer Details' + expect(page).to have_selector 'h1', text: 'Customer Details' o = Spree::Order.last - o.distributor.should == distributor1 - o.order_cycle.should == order_cycle1 + expect(o.distributor).to eq distributor1 + expect(o.order_cycle).to eq order_cycle1 end end diff --git a/spec/features/admin/overview_spec.rb b/spec/features/admin/overview_spec.rb index 534f784ee0..184d68c80b 100644 --- a/spec/features/admin/overview_spec.rb +++ b/spec/features/admin/overview_spec.rb @@ -16,7 +16,7 @@ feature %q{ Spree::Admin::OverviewController.any_instance.stub(:spree_current_user).and_return @enterprise_user quick_login_as @enterprise_user end - + context "with no enterprises" do it "prompts the user to create a new enteprise" do visit '/admin' @@ -42,9 +42,8 @@ feature %q{ page.should have_selector ".dashboard_item#order_cycles" page.should have_selector ".dashboard_item#enterprises .list-item", text: d1.name page.should have_selector ".dashboard_item#enterprises .button.bottom", text: "MANAGE MY ENTERPRISES" - end - + context "but no products or order cycles" do it "prompts the user to create a new product and to manage order cycles" do visit '/admin' diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index 201381f852..1f61b07d2a 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -86,11 +86,14 @@ feature %q{ context "as an enterprise user" do - before(:each) do + before do @new_user = create_enterprise_user @supplier2 = create(:supplier_enterprise, name: 'Another Supplier') + @supplier_permitted = create(:supplier_enterprise, name: 'Permitted Supplier') @new_user.enterprise_roles.build(enterprise: @supplier2).save @new_user.enterprise_roles.build(enterprise: @distributors[0]).save + create(:enterprise_relationship, parent: @supplier_permitted, child: @supplier2, + permissions_list: [:manage_products]) login_to_admin_as @new_user end @@ -116,9 +119,8 @@ feature %q{ select taxon.name, from: "product_primary_taxon_id" # Should only have suppliers listed which the user can manage - within "#product_supplier_id" do - page.should_not have_content @supplier.name - end + page.should have_select 'product_supplier_id', with_options: [@supplier2.name, @supplier_permitted.name] + page.should_not have_select 'product_supplier_id', with_options: [@supplier.name] click_button 'Create' @@ -127,6 +129,18 @@ feature %q{ product.supplier.should == @supplier2 end + scenario "editing a product" do + product = create(:simple_product, name: 'a product', supplier: @supplier2) + + visit spree.edit_admin_product_path product + + select 'Permitted Supplier', from: 'product_supplier_id' + click_button 'Update' + flash_message.should == 'Product "a product" has been successfully updated!' + product.reload + product.supplier.should == @supplier_permitted + end + scenario "editing product distributions" do product = create(:simple_product, supplier: @supplier2) diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb index cf0bc658bd..f8b405374e 100644 --- a/spec/features/consumer/authentication_spec.rb +++ b/spec/features/consumer/authentication_spec.rb @@ -10,7 +10,6 @@ feature "Authentication", js: true do visit groups_path(anchor: "login?after_login=#{producers_path}") fill_in "Email", with: user.email fill_in "Password", with: user.password - save_screenshot "/Users/willmarshall/Desktop/wtf.png" click_login_button page.should have_content "Find local producers" current_path.should == producers_path diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb new file mode 100644 index 0000000000..3500736100 --- /dev/null +++ b/spec/features/consumer/registration_spec.rb @@ -0,0 +1,145 @@ +require 'spec_helper' + +feature "Registration", js: true do + include WebHelper + + describe "Registering a Profile" do + let(:user) { create(:user, password: "password", password_confirmation: "password") } + + it "Allows a logged in user to register a profile" do + visit registration_path + + expect(URI.parse(current_url).path).to eq registration_auth_path + + page.has_selector? "dd", text: "Log in" + switch_to_login_tab + + # Enter Login details + fill_in "Email", with: user.email + fill_in "Password", with: user.password + click_login_and_ensure_content "This wizard will step you through creating a profile" + + expect(URI.parse(current_url).path).to eq registration_path + + # Done reading introduction + click_button_and_ensure_content "Let's get started!", "Woot! First we need to know what sort of enterprise you are:" + + # Filling in details + fill_in 'enterprise_name', with: "My Awesome Enterprise" + click_link 'both-panel' + + click_button_and_ensure_content "Continue", "Greetings My Awesome Enterprise" + + # Filling in address + fill_in 'enterprise_address', with: '123 Abc Street' + fill_in 'enterprise_city', with: 'Northcote' + fill_in 'enterprise_zipcode', with: '3070' + select 'Australia', from: 'enterprise_country' + select 'Vic', from: 'enterprise_state' + click_button 'Continue' + + # Filling in Contact Details + expect(page).to have_content 'Who is responsible for managing My Awesome Enterprise?' + fill_in 'enterprise_contact', with: 'Saskia Munroe' + page.should have_field 'enterprise_email', with: user.email + fill_in 'enterprise_phone', with: '12 3456 7890' + click_button 'Continue' + + # Enterprise should be created + expect(page).to have_content 'Nice one!' + e = Enterprise.find_by_name('My Awesome Enterprise') + expect(e.address.address1).to eq "123 Abc Street" + expect(e.is_distributor).to eq true + expect(e.is_primary_producer).to eq true + expect(e.contact).to eq "Saskia Munroe" + + # Filling in about + fill_in 'enterprise_description', with: 'Short description' + fill_in 'enterprise_long_desc', with: 'Long description' + fill_in 'enterprise_abn', with: '12345' + fill_in 'enterprise_acn', with: '54321' + click_button 'Continue' + + # Enterprise should be updated + expect(page).to have_content 'Last step!' + e.reload + expect(e.description).to eq "Short description" + expect(e.long_description).to eq "Long description" + expect(e.abn).to eq '12345' + expect(e.acn).to eq '54321' + + # Filling in social + fill_in 'enterprise_website', with: 'www.shop.com' + fill_in 'enterprise_facebook', with: 'FaCeBoOk' + fill_in 'enterprise_linkedin', with: 'LiNkEdIn' + fill_in 'enterprise_twitter', with: '@TwItTeR' + fill_in 'enterprise_instagram', with: '@InStAgRaM' + click_button 'Continue' + + # Done + expect(page).to have_content "You have successfully completed the profile for My Awesome Enterprise" + e.reload + expect(e.website).to eq "www.shop.com" + expect(e.facebook).to eq "FaCeBoOk" + expect(e.linkedin).to eq "LiNkEdIn" + expect(e.twitter).to eq "@TwItTeR" + expect(e.instagram).to eq "@InStAgRaM" + end + + it "Allows a logged in user to register a store" do + visit store_registration_path + + expect(URI.parse(current_url).path).to eq registration_auth_path + + page.has_selector? "dd", text: "Log in" + switch_to_login_tab + + # Enter Login details + fill_in "Email", with: user.email + fill_in "Password", with: user.password + click_login_and_ensure_content "This wizard will step you through creating a profile" + + expect(URI.parse(current_url).path).to eq store_registration_path + + # Done reading introduction + click_button_and_ensure_content "Let's get started!", "Woot! First we need to know the name of your farm:" + + # Details Page + expect(page).to have_content "Woot! First we need to know the name of your farm:" + expect(page).to_not have_selector '#enterprise-types' + + # Everything from here should be covered in 'profile' spec + end + end + + def switch_to_login_tab + # Link appears to be unresponsive for a while, so keep clicking it until it works + using_wait_time 0.5 do + 10.times do + click_link "Log in" + break if page.has_selector? "dd.active", text: "Log in" + end + end + end + + def click_login_and_ensure_content(content) + # Buttons appear to be unresponsive for a while, so keep clicking them until content appears + using_wait_time 1 do + 3.times do + click_button "Log in" + break if page.has_selector? "div#loading", text: "Hold on a moment, we're logging you in" + end + end + expect(page).to have_content content + end + + def click_button_and_ensure_content(button_text, content) + # Buttons appear to be unresponsive for a while, so keep clicking them until content appears + using_wait_time 0.5 do + 10.times do + click_button button_text + break if page.has_content? content + end + end + end +end diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index ab53befda5..f9c05651a2 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -28,7 +28,7 @@ feature "As a consumer I want to check out my cart", js: true do end describe "with shipping methods" do - let(:sm1) { create(:shipping_method, require_ship_address: true, name: "Frogs", description: "yellow") } + let(:sm1) { create(:shipping_method, require_ship_address: true, name: "Frogs", description: "yellow", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 0.00)) } let(:sm2) { create(:shipping_method, require_ship_address: false, name: "Donkeys", description: "blue", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 4.56)) } before do distributor.shipping_methods << sm1 @@ -188,7 +188,7 @@ feature "As a consumer I want to check out my cart", js: true do # Order should have a payment with the correct amount o = Spree::Order.complete.first - o.payments.first.amount.should == 15.79 + o.payments.first.amount.should == 11.23 end it "shows the payment processing failed message when submitted with an invalid credit card" do diff --git a/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee b/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee index 9701377a73..d179f6857c 100644 --- a/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee @@ -12,5 +12,5 @@ describe "enterprise relationships", -> EnterpriseRelationships = _EnterpriseRelationships_ it "presents permission names", -> - expect(EnterpriseRelationships.permission_presentation("add_to_order_cycle")).toEqual "can add to order cycle" - expect(EnterpriseRelationships.permission_presentation("manage_products")).toEqual "can manage the products of" + expect(EnterpriseRelationships.permission_presentation("add_to_order_cycle")).toEqual "to add to order cycle" + expect(EnterpriseRelationships.permission_presentation("manage_products")).toEqual "to manage products" diff --git a/spec/javascripts/unit/bulk_order_management_spec.js.coffee b/spec/javascripts/unit/bulk_order_management_spec.js.coffee index 80884b855d..35b9e13d61 100644 --- a/spec/javascripts/unit/bulk_order_management_spec.js.coffee +++ b/spec/javascripts/unit/bulk_order_management_spec.js.coffee @@ -2,7 +2,9 @@ describe "AdminOrderMgmtCtrl", -> ctrl = scope = httpBackend = VariantUnitManager = null beforeEach -> - module "ofn.admin" + module "ofn.admin", ($provide) -> + $provide.value 'SpreeApiKey', 'API_KEY' + return beforeEach inject(($controller, $rootScope, $httpBackend, _VariantUnitManager_) -> scope = $rootScope.$new() ctrl = $controller @@ -18,7 +20,7 @@ describe "AdminOrderMgmtCtrl", -> returnedSuppliers = ["list of suppliers"] returnedDistributors = ["list of distributors"] returnedOrderCycles = [ "oc1", "oc2", "oc3" ] - httpBackend.expectGET("/api/users/authorise_api?token=api_key").respond success: "Use of API Authorised" + httpBackend.expectGET("/api/users/authorise_api?token=API_KEY").respond success: "Use of API Authorised" httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[is_primary_producer_eq]=true").respond returnedSuppliers httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[is_distributor_eq]=true").respond returnedDistributors httpBackend.expectGET("/api/order_cycles/accessible").respond returnedOrderCycles @@ -27,7 +29,7 @@ describe "AdminOrderMgmtCtrl", -> #spyOn(returnedSuppliers, "unshift") #spyOn(returnedDistributors, "unshift") #spyOn(returnedOrderCycles, "unshift") - scope.initialise "api_key" + scope.initialise() httpBackend.flush() expect(scope.suppliers).toEqual [{ id : '0', name : 'All' }, 'list of suppliers'] diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index b9081a1882..649ab99f52 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -165,7 +165,7 @@ describe "filtering products for submission to database", -> variant_unit: 'weight' variant_unit_scale: 1 ] - + # TODO Not an exhaustive test, is there a better way to do this? it "only returns the properties of products which ought to be updated", -> available_on = new Date() @@ -238,6 +238,7 @@ describe "AdminProductEditCtrl", -> module ($provide)-> $provide.value "producers", [] $provide.value "taxons", [] + $provide.value 'SpreeApiKey', 'API_KEY' null beforeEach inject((_$controller_, _$timeout_, $rootScope, _$httpBackend_, _DirtyProducts_) -> @@ -252,9 +253,9 @@ describe "AdminProductEditCtrl", -> describe "loading data upon initialisation", -> it "gets a list of producers and then resets products with a list of data", -> - $httpBackend.expectGET("/api/users/authorise_api?token=api_key").respond success: "Use of API Authorised" + $httpBackend.expectGET("/api/users/authorise_api?token=API_KEY").respond success: "Use of API Authorised" spyOn($scope, "fetchProducts").andReturn "nothing" - $scope.initialise "api_key" + $scope.initialise() $httpBackend.flush() expect($scope.fetchProducts.calls.length).toEqual 1 expect($scope.spree_api_key_ok).toEqual true diff --git a/spec/javascripts/unit/darkswarm/filters/localize_currency_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/localize_currency_spec.js.coffee new file mode 100644 index 0000000000..0d21c7de6c --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/localize_currency_spec.js.coffee @@ -0,0 +1,42 @@ +describe 'convert number to localised currency ', -> + filter = currencyconfig = null + + beforeEach -> + currencyconfig = + symbol: "$" + symbol_position: "before" + currency: "D" + hide_cents: "false" + # Not used yet... + # decimal_mark: "." + # thousands_separator: "," + module 'Darkswarm' + module ($provide)-> + $provide.value "currencyConfig", currencyconfig + null + inject ($filter) -> + filter = $filter('localizeCurrency') + + it "adds decimal fraction to an amount", -> + expect(filter(10)).toEqual "$10.00" + + it "handles an existing fraction", -> + expect(filter(9.9)).toEqual "$9.90" + + it "can use any currency symbol", -> + currencyconfig.symbol = "£" + expect(filter(404.04)).toEqual "£404.04" + + it "can place symbols after the amount", -> + currencyconfig.symbol_position = "after" + expect(filter(333.3)).toEqual "333.30 $" + + it "can add a currency string", -> + currencyconfig.display_currency = "true" + expect(filter(5)).toEqual "$5.00 D" + + it "can hide cents", -> + currencyconfig.hide_cents = "true" + expect(filter(5)).toEqual "$5" + + diff --git a/spec/javascripts/unit/darkswarm/services/enterprise_registration_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/enterprise_registration_spec.js.coffee new file mode 100644 index 0000000000..1852dbd16c --- /dev/null +++ b/spec/javascripts/unit/darkswarm/services/enterprise_registration_spec.js.coffee @@ -0,0 +1,85 @@ +describe "EnterpriseRegistrationService", -> + EnterpriseRegistrationService = null + $httpBackend = null + availableCountries = [] + enterpriseAttributes = + name: "Enterprise 1" + something: true + spreeApiKey = "keykeykeykey" + CurrentUser = + id: 2 + email: 'lalala@email.com' + RegistrationServiceMock = + select: -> null + + beforeEach -> + module('Darkswarm') + angular.module('Darkswarm').value 'availableCountries', availableCountries + angular.module('Darkswarm').value 'enterpriseAttributes', enterpriseAttributes + angular.module('Darkswarm').value 'spreeApiKey', spreeApiKey + angular.module('Darkswarm').value 'CurrentUser', CurrentUser + angular.module('Darkswarm').value 'RegistrationService', RegistrationServiceMock + + inject ($injector, _$httpBackend_) -> + $httpBackend = _$httpBackend_ + EnterpriseRegistrationService = $injector.get("EnterpriseRegistrationService") + + it "adds the specified attributes to the ERS enterprise object", -> + expect(EnterpriseRegistrationService.enterprise.name).toBe "Enterprise 1" + expect(EnterpriseRegistrationService.enterprise.something).toBe true + + describe "creating an enterprise", -> + describe "success", -> + beforeEach -> + spyOn(RegistrationServiceMock, "select") + $httpBackend.expectPOST("/api/enterprises?token=keykeykeykey").respond 200, 6 + EnterpriseRegistrationService.create() + $httpBackend.flush() + + it "stores the id of the created enterprise", -> + expect(EnterpriseRegistrationService.enterprise.id).toBe 6 + + it "moves the user to the about page", -> + expect(RegistrationServiceMock.select).toHaveBeenCalledWith 'about' + + describe "failure", -> + beforeEach -> + spyOn(RegistrationServiceMock, "select") + spyOn(window, "alert") + $httpBackend.expectPOST("/api/enterprises?token=keykeykeykey").respond 400, 6 + EnterpriseRegistrationService.create() + $httpBackend.flush() + + it "alerts the user to failure", -> + expect(window.alert).toHaveBeenCalledWith 'Failed to create your enterprise.\nPlease ensure all fields are completely filled out.' + + it "does not move the user to the about page", -> + expect(RegistrationServiceMock.select).not.toHaveBeenCalled + + + describe "updating an enterprise", -> + beforeEach -> + EnterpriseRegistrationService.enterprise.id = 78 + spyOn(RegistrationServiceMock, "select") + + describe "success", -> + beforeEach -> + $httpBackend.expectPUT("/api/enterprises/78?token=keykeykeykey").respond 200, 6 + EnterpriseRegistrationService.update('step') + $httpBackend.flush() + + it "moves the user to the about page", -> + expect(RegistrationServiceMock.select).toHaveBeenCalledWith 'step' + + describe "failure", -> + beforeEach -> + spyOn(window, "alert") + $httpBackend.expectPUT("/api/enterprises/78?token=keykeykeykey").respond 400, 6 + EnterpriseRegistrationService.update('step') + $httpBackend.flush() + + it "alerts the user to failure", -> + expect(window.alert).toHaveBeenCalledWith 'Failed to update your enterprise.\nPlease ensure all fields are completely filled out.' + + it "does not move the user to the about page", -> + expect(RegistrationServiceMock.select).not.toHaveBeenCalled diff --git a/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee index 6b1238d1d1..f8ae8230bc 100644 --- a/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee @@ -8,18 +8,21 @@ describe "Hubs service", -> active: false orders_close_at: new Date() is_distributor: true + has_shopfront: true } { id: 3 active: false orders_close_at: new Date() is_distributor: true + has_shopfront: true } { id: 1 active: true orders_close_at: new Date() is_distributor: true + has_shopfront: true } ] diff --git a/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee index ac9865a142..5c6e138e0d 100644 --- a/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee @@ -5,8 +5,8 @@ describe 'Variants service', -> beforeEach -> variant = id: 1 - base_price: 80.5 - price: 100 + price: 80.5 + price_with_fees: 100 module 'Darkswarm' inject ($injector)-> Variants = $injector.get("Variants") diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 0d5127314a..2a8d001804 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -516,30 +516,44 @@ describe 'OrderCycle services', -> ] describe 'removing exchanges', -> - it 'removes incoming exchanges', -> - exchange = {enterprise_id: '123', active: true, variants: {}, enterprise_fees: []} - OrderCycle.order_cycle.incoming_exchanges = [exchange] - OrderCycle.removeExchange(exchange) - expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [] + exchange = null - it 'removes outgoing exchanges', -> - exchange = {enterprise_id: '123', active: true, variants: {}, enterprise_fees: []} - OrderCycle.order_cycle.outgoing_exchanges = [exchange] - OrderCycle.removeExchange(exchange) - expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [] - - it 'removes distribution of all exchange variants', -> + beforeEach -> spyOn(OrderCycle, 'removeDistributionOfVariant') exchange = enterprise_id: '123' active: true + incoming: false variants: {1: true, 2: false, 3: true} enterprise_fees: [] - OrderCycle.order_cycle.incoming_exchanges = [exchange] - OrderCycle.removeExchange(exchange) - expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('1') - expect(OrderCycle.removeDistributionOfVariant).not.toHaveBeenCalledWith('2') - expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('3') + + describe "removing incoming exchanges", -> + beforeEach -> + exchange.incoming = true + OrderCycle.order_cycle.incoming_exchanges = [exchange] + + it 'removes the exchange', -> + OrderCycle.removeExchange(exchange) + expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [] + + it 'removes distribution of all exchange variants', -> + OrderCycle.removeExchange(exchange) + expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('1') + expect(OrderCycle.removeDistributionOfVariant).not.toHaveBeenCalledWith('2') + expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('3') + + describe "removing outgoing exchanges", -> + beforeEach -> + exchange.incoming = false + OrderCycle.order_cycle.outgoing_exchanges = [exchange] + + it 'removes the exchange', -> + OrderCycle.removeExchange(exchange) + expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [] + + it "does not remove distribution of any variants", -> + OrderCycle.removeExchange(exchange) + expect(OrderCycle.removeDistributionOfVariant).not.toHaveBeenCalled() it 'adds coordinator fees', -> OrderCycle.addCoordinatorFee() diff --git a/spec/mailers/enterprise_mailer_spec.rb b/spec/mailers/enterprise_mailer_spec.rb new file mode 100644 index 0000000000..412870bad7 --- /dev/null +++ b/spec/mailers/enterprise_mailer_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe EnterpriseMailer do + before do + @enterprise = create(:enterprise) + ActionMailer::Base.deliveries = [] + end + + it "should send an email when given an enterprise" do + EnterpriseMailer.creation_confirmation(@enterprise).deliver + ActionMailer::Base.deliveries.count.should == 1 + end +end \ No newline at end of file diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 25282dcb69..2fd814c3e3 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -1,8 +1,10 @@ require 'spec_helper' describe Enterprise do + include AuthenticationWorkflow describe "associations" do + it { should belong_to(:owner) } it { should have_many(:supplied_products) } it { should have_many(:distributed_orders) } it { should belong_to(:address) } @@ -51,11 +53,44 @@ describe Enterprise do e.suppliers end end + + describe "ownership" do + let(:u1) { create_enterprise_user } + let(:u2) { create_enterprise_user } + let!(:e) { create(:enterprise, owner: u1 ) } + + it "adds new owner to list of managers" do + expect(e.owner).to eq u1 + expect(e.users).to include u1 + expect(e.users).to_not include u2 + e.owner = u2 + e.save! + e.reload + expect(e.owner).to eq u2 + expect(e.users).to include u1, u2 + end + + it "validates ownership limit" do + expect(u1.enterprise_limit).to be 1 + expect(u1.owned_enterprises(:reload)).to eq [e] + e2 = create(:enterprise, owner: u2 ) + expect{ + e2.owner = u1 + e2.save! + }.to raise_error ActiveRecord::RecordInvalid, "Validation failed: You are not permitted to own own any more enterprises (limit is 1)." + end + end end describe "validations" do subject { FactoryGirl.create(:distributor_enterprise, :address => FactoryGirl.create(:address)) } it { should validate_presence_of(:name) } + + it "requires an owner" do + expect{ + e = create(:enterprise, owner: nil) + }.to raise_error ActiveRecord::RecordInvalid, "Validation failed: Owner can't be blank" + end end describe "delegations" do @@ -74,7 +109,7 @@ describe Enterprise do Enterprise.visible.should == [s1] end end - + describe "distributors_with_active_order_cycles" do it "finds active distributors by order cycles" do s = create(:supplier_enterprise) @@ -441,8 +476,8 @@ describe Enterprise do end describe "presentation of attributes" do - let(:distributor) { - create(:distributor_enterprise, + let(:distributor) { + create(:distributor_enterprise, website: "http://www.google.com", facebook: "www.facebook.com/roger", linkedin: "https://linkedin.com") diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index 4e9ad94fac..f049c64b2f 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -1,6 +1,30 @@ describe Spree.user_class do - context "#create" do + describe "associations" do + it { should have_many(:owned_enterprises) } + describe "enterprise ownership" do + let(:u1) { create(:user, enterprise_limit: 2) } + let(:u2) { create(:user, enterprise_limit: 1) } + let!(:e1) { create(:enterprise, owner: u1) } + let!(:e2) { create(:enterprise, owner: u1) } + + it "provides access to owned enterprises" do + expect(u1.owned_enterprises(:reload)).to include e1, e2 + end + + it "enforces the limit on the number of enterprise owned" do + expect(u2.owned_enterprises(:reload)).to eq [] + u2.owned_enterprises << e1 + expect(u2.save!).to_not raise_error + expect { + u2.owned_enterprises << e2 + u2.save! + }.to raise_error ActiveRecord::RecordInvalid, "Validation failed: The nominated user is not permitted to own own any more enterprises (limit is 1)." + end + end + end + + context "#create" do it "should send a signup email" do Spree::UserMailer.should_receive(:signup_confirmation).and_return(double(:deliver => true)) create(:user) diff --git a/spec/support/request/authentication_workflow.rb b/spec/support/request/authentication_workflow.rb index add8de9f28..a2ee597f27 100644 --- a/spec/support/request/authentication_workflow.rb +++ b/spec/support/request/authentication_workflow.rb @@ -6,7 +6,7 @@ module AuthenticationWorkflow def quick_login_as_admin admin_role = Spree::Role.find_or_create_by_name!('admin') - admin_user = create(:user, + admin_user = create(:user, :password => 'passw0rd', :password_confirmation => 'passw0rd', :remember_me => false, @@ -25,7 +25,7 @@ module AuthenticationWorkflow def login_to_admin_section admin_role = Spree::Role.find_or_create_by_name!('admin') - admin_user = create(:user, + admin_user = create(:user, :password => 'passw0rd', :password_confirmation => 'passw0rd', :remember_me => false, @@ -38,7 +38,7 @@ module AuthenticationWorkflow end def create_enterprise_user(enterprises = []) - new_user = create(:user, email: 'enterprise@hub.com', password: 'blahblah', :password_confirmation => 'blahblah', ) + new_user = create(:user, password: 'blahblah', :password_confirmation => 'blahblah') new_user.spree_roles = [] # for some reason unbeknown to me, this new user gets admin permissions by default. for enterprise in enterprises do new_user.enterprise_roles.build(enterprise: enterprise).save