diff --git a/Gemfile b/Gemfile index 6d3c7262c8..b63961a3ef 100644 --- a/Gemfile +++ b/Gemfile @@ -28,7 +28,7 @@ gem 'angularjs-rails', '1.2.13' gem 'bugsnag' gem 'newrelic_rpm' gem 'haml' -gem 'sass', "~> 3.2" +gem 'sass', "~> 3.3" gem 'sass-rails', '~> 3.2.3', groups: [:default, :assets] gem 'aws-sdk' gem 'db2fog' diff --git a/Gemfile.lock b/Gemfile.lock index d2a20aed31..17dbfdf8c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -177,7 +177,7 @@ GEM celluloid (0.15.2) timers (~> 1.1.0) chronic (0.10.2) - chunky_png (1.3.0) + chunky_png (1.3.4) climate_control (0.0.3) activesupport (>= 3.0) cliver (0.3.2) @@ -197,12 +197,22 @@ GEM active_link_to (~> 1.0.0) paperclip (>= 2.3.0) rails (>= 3.0.0) - compass (0.12.4) + compass (1.0.3) chunky_png (~> 1.2) - fssm (>= 0.2.7) - sass (~> 3.2.17) - compass-rails (1.0.3) - compass (>= 0.12.2, < 0.14) + compass-core (~> 1.0.2) + compass-import-once (~> 1.0.5) + rb-fsevent (>= 0.9.3) + rb-inotify (>= 0.9) + sass (>= 3.3.13, < 3.5) + compass-core (1.0.3) + multi_json (~> 1.0) + sass (>= 3.3.0, < 3.5) + compass-import-once (1.0.5) + sass (>= 3.2, < 3.5) + compass-rails (2.0.4) + compass (~> 1.0.0) + sass-rails (<= 5.0.1) + sprockets (< 2.13) crack (0.4.1) safe_yaml (~> 0.9.0) css_parser (1.3.5) @@ -267,10 +277,9 @@ GEM foundation-icons-sass-rails (3.0.0) railties (>= 3.1.1) sass-rails (>= 3.1.1) - foundation-rails (5.2.2.0) + foundation-rails (5.5.0.0) railties (>= 3.1.0) - sass (>= 3.2.0) - fssm (0.2.10) + sass (>= 3.2.0, < 3.4) fuubar (1.3.3) rspec (>= 2.14.0, < 3.1.0) ruby-progressbar (~> 1.4) @@ -338,7 +347,7 @@ GEM railties (>= 3.1) money (5.1.1) i18n (~> 0.6.0) - multi_json (1.10.1) + multi_json (1.11.0) multi_xml (0.5.5) net-scp (1.1.2) net-ssh (>= 2.6.5) @@ -448,7 +457,7 @@ GEM ruby-hmac (0.4.0) ruby-progressbar (1.7.1) safe_yaml (0.9.5) - sass (3.2.19) + sass (3.3.14) sass-rails (3.2.6) railties (~> 3.2.0) sass (>= 3.1.10) @@ -577,7 +586,7 @@ DEPENDENCIES representative_view roadie-rails (~> 1.0.3) rspec-rails - sass (~> 3.2) + sass (~> 3.3) sass-rails (~> 3.2.3) shoulda-matchers simple_form! diff --git a/app/assets/images/noimage/large.png b/app/assets/images/noimage/large.png index c9b2af198c..29dcff5ea9 100644 Binary files a/app/assets/images/noimage/large.png and b/app/assets/images/noimage/large.png differ diff --git a/app/assets/images/noimage/mini.png b/app/assets/images/noimage/mini.png index dac04c5863..5094c92a18 100644 Binary files a/app/assets/images/noimage/mini.png and b/app/assets/images/noimage/mini.png differ diff --git a/app/assets/images/noimage/product.png b/app/assets/images/noimage/product.png index 7bb97178b9..ca06da639b 100644 Binary files a/app/assets/images/noimage/product.png and b/app/assets/images/noimage/product.png differ diff --git a/app/assets/images/noimage/small.png b/app/assets/images/noimage/small.png index 832878c107..ca06da639b 100644 Binary files a/app/assets/images/noimage/small.png and b/app/assets/images/noimage/small.png differ diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index 45acfd6523..68bc01b6c1 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -36,4 +36,4 @@ $ -> # Hacky fix for issue - http://foundation.zurb.com/forum/posts/2112-foundation-5100-syntax-error-in-js Foundation.set_namespace "" - #$(document).foundation() + $(document).foundation() diff --git a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee index 019a09e96f..8443301765 100644 --- a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Products, OrderCycle, FilterSelectorsService, Cart) -> +Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Products, OrderCycle, FilterSelectorsService, Cart, Taxons, Properties) -> $scope.Products = Products $scope.Cart = Cart $scope.totalActive = FilterSelectorsService.totalActive @@ -9,6 +9,9 @@ Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Products, OrderCycle, $scope.limit = 3 $scope.order_cycle = OrderCycle.order_cycle + $scope.$watch "Products.loading", (newValue, oldValue) -> + $scope.$broadcast("loadFilterSelectors") if !newValue + $scope.incrementLimit = -> if $scope.limit < Products.products.length $scope.limit = $scope.limit + 1 @@ -17,3 +20,17 @@ Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Products, OrderCycle, code = e.keyCode || e.which if code == 13 e.preventDefault() + + $scope.appliedTaxonsList = -> + $scope.activeTaxons.map( (taxon_id) -> + Taxons.taxons_by_id[taxon_id].name + ).join(" & ") if $scope.activeTaxons? + + $scope.appliedPropertiesList = -> + $scope.activeProperties.map( (property_id) -> + Properties.properties_by_id[property_id].name + ).join(" & ") if $scope.activeProperties? + + $scope.clearAll = -> + $scope.query = "" + FilterSelectorsService.clearAll() diff --git a/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/filter_selector.js.coffee similarity index 52% rename from app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee rename to app/assets/javascripts/darkswarm/directives/filter_selector.js.coffee index 633ecd22f4..b964219925 100644 --- a/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/filter_selector.js.coffee @@ -1,43 +1,51 @@ -Darkswarm.directive "taxonSelector", (FilterSelectorsService)-> +Darkswarm.directive "filterSelector", (FilterSelectorsService)-> # Automatically builds activeSelectors for taxons # Lots of magic here restrict: 'E' replace: true scope: objects: "&" - results: "=" - templateUrl: "taxon_selector.html" + activeSelectors: "=" + allSelectors: "=?" # Optional + templateUrl: "filter_selector.html" link: (scope, elem, attr)-> selectors_by_id = {} selectors = null # To get scoping/closure right scope.emit = -> - scope.results = selectors.filter (selector)-> + scope.activeSelectors = selectors.filter (selector)-> selector.active .map (selector)-> - selector.taxon.id + selector.object.id - # Build hash of unique taxons, each of which gets an ActiveSelector + # This can be called from a parent scope + # when data has been loaded, in order to pass + # selectors up + scope.$on 'loadFilterSelectors', -> + scope.allSelectors = scope.selectors() + + scope.$watchCollection "selectors()", (newValue, oldValue) -> + scope.allSelectors = scope.selectors() + + # Build a list of selectors scope.selectors = -> - taxons = {} - selectors = [] - for object in scope.objects() - for taxon in object.taxons - taxons[taxon.id] = taxon - if object.supplied_taxons - for taxon in object.supplied_taxons - taxons[taxon.id] = taxon - - # Generate a selector for each taxon. + # Generate a selector for each object. # NOTE: THESE ARE MEMOIZED to stop new selectors from being created constantly, otherwise function always returns non-identical results # This means the $digest cycle can never close and times out # See http://stackoverflow.com/questions/19306452/how-to-fix-10-digest-iterations-reached-aborting-error-in-angular-1-2-fil - for id, taxon of taxons + selectors = [] + for id, object of scope.objects() if selector = selectors_by_id[id] selectors.push selector else selector = selectors_by_id[id] = FilterSelectorsService.new - taxon: taxon + object: object selectors.push selector selectors + + scope.ifDefined = (value, if_undefined) -> + if angular.isDefined(value) + value + else + if_undefined diff --git a/app/assets/javascripts/darkswarm/directives/single_line_selectors.coffee b/app/assets/javascripts/darkswarm/directives/single_line_selectors.coffee new file mode 100644 index 0000000000..97e03cb689 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/single_line_selectors.coffee @@ -0,0 +1,72 @@ +Darkswarm.directive 'singleLineSelectors', ($timeout, $filter) -> + restrict: 'E' + templateUrl: "single_line_selectors.html" + scope: + objects: "&" + activeSelectors: "=" + link: (scope,element,attrs) -> + scope.fitting = false + + scope.overFlowSelectors = -> + return [] unless scope.allSelectors? + $filter('filter')(scope.allSelectors, { fits: false }) + + scope.selectedOverFlowSelectors = -> + $filter('filter')(scope.overFlowSelectors(), { active: true }) + + # had to duplicate this to make overflow selectors work + scope.emit = -> + scope.activeSelectors = scope.allSelectors.filter (selector)-> + selector.active + .map (selector)-> + selector.object.id + + # From: http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing + debouncer = (func, timeout) -> + timeoutID = undefined + timeout = timeout or 50 + -> + subject = this + args = arguments + clearTimeout timeoutID + timeoutID = setTimeout(-> + func.apply subject, Array::slice.call(args) + , timeout) + + loadWidths = -> + $(element).find("li").not(".more").each (i) -> + scope.allSelectors[i].width = $(this).outerWidth(true) + return null # So we don't exit the loop weirdly + + + fit = -> + used = $(element).find("li.more").outerWidth(true) + used += selector.width for selector in scope.allSelectors when selector.fits + available = $(element).parent(".filter-shopfront").innerWidth() - used + if available > 0 + for selector in scope.allSelectors when !selector.fits + available -= selector.width + selector.fits = true if available > 0 + else + if scope.allSelectors.length > 0 + for i in [scope.allSelectors.length-1..0] + selector = scope.allSelectors[i] + if !selector.fits + continue + else + if available < 0 + selector.fits = false + available += selector.width + scope.fitting = false + + scope.$watchCollection "allSelectors", -> + if scope.allSelectors? + scope.fitting = true + selector.fits = true for selector in scope.allSelectors + $timeout(loadWidths, 0, true).then -> + $timeout fit, 0, true + + $(window).resize debouncer (e) -> + scope.fitting = true + if scope.allSelectors? + $timeout fit, 0, true diff --git a/app/assets/javascripts/darkswarm/filters/properties.js.coffee b/app/assets/javascripts/darkswarm/filters/properties.js.coffee new file mode 100644 index 0000000000..51b02e6634 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/properties.js.coffee @@ -0,0 +1,16 @@ +Darkswarm.filter 'properties', ()-> + # Filter anything that responds to object.properties + (objects, ids) -> + objects ||= [] + ids ?= [] + if ids.length == 0 + # No properties selected, pass all objects through. + objects + else + objects.filter (obj)-> + properties = obj.properties + # Combine object properties with supplied properties, if they exist. + # properties = properties.concat obj.supplied_properties if obj.supplied_properties + # Match property array. + properties.some (property)-> + property.id in ids diff --git a/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee b/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee new file mode 100644 index 0000000000..4ab8c94bfd --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee @@ -0,0 +1,7 @@ +Darkswarm.filter 'propertiesOf', -> + (objects)-> + properties = {} + for object in objects + for property in object.properties + properties[property.id] = property + properties diff --git a/app/assets/javascripts/darkswarm/filters/taxons_of.js.coffee b/app/assets/javascripts/darkswarm/filters/taxons_of.js.coffee new file mode 100644 index 0000000000..6726a5c22e --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/taxons_of.js.coffee @@ -0,0 +1,10 @@ +Darkswarm.filter 'taxonsOf', -> + (objects)-> + taxons = {} + for object in objects + for taxon in object.taxons + taxons[taxon.id] = taxon + if object.supplied_taxons + for taxon in object.supplied_taxons + taxons[taxon.id] = taxon + taxons diff --git a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee index f2d7049e67..c83022b579 100644 --- a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee +++ b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee @@ -25,7 +25,7 @@ Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redir active: Navigation.active close: -> - if location.pathname == "/" + if location.pathname in ["/", "/checkout"] Navigation.navigate "/" else Loading.message = "Taking you back to the home page" diff --git a/app/assets/javascripts/darkswarm/services/products.js.coffee b/app/assets/javascripts/darkswarm/services/products.js.coffee index 9da3187f82..db8bbbb7ee 100644 --- a/app/assets/javascripts/darkswarm/services/products.js.coffee +++ b/app/assets/javascripts/darkswarm/services/products.js.coffee @@ -1,27 +1,28 @@ -Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Cart, Variants) -> +Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Properties, Cart, Variants) -> new class Products constructor: -> @update() - + # TODO: don't need to scope this into object # Already on object as far as controller scope is concerned products: null loading: true update: => - @loading = true + @loading = true @products = $resource("/shop/products").query (products)=> @extend() && @dereference() - @registerVariants() + @registerVariants() @registerVariantsWithCart() @loading = false @ - + dereference: -> for product in @products product.supplier = Enterprises.enterprises_by_id[product.supplier.id] Dereferencer.dereference product.taxons, Taxons.taxons_by_id - + Dereferencer.dereference product.properties, Properties.properties_by_id + # May return different objects! If the variant has already been registered # by another service, we fetch those registerVariants: -> @@ -45,7 +46,7 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Car prices = (v.price for v in product.variants) product.price = Math.min.apply(null, prices) product.hasVariants = product.variants?.length > 0 - + product.primaryImage = product.images[0]?.small_url if product.images product.primaryImageOrMissing = product.primaryImage || "/assets/noimage/small.png" product.largeImage = product.images[0]?.large_url if product.images diff --git a/app/assets/javascripts/darkswarm/services/properties.js.coffee b/app/assets/javascripts/darkswarm/services/properties.js.coffee new file mode 100644 index 0000000000..37314947b4 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/properties.js.coffee @@ -0,0 +1,9 @@ +Darkswarm.factory "Properties", (properties)-> + new class Properties + # Populate ProductProperties.properties from json in page. + properties: properties + properties_by_id: {} + constructor: -> + # Map properties to id/object pairs for lookup. + for property in @properties + @properties_by_id[property.id] = property diff --git a/app/assets/javascripts/templates/active_selector.html.haml b/app/assets/javascripts/templates/active_selector.html.haml index 14b12bbd9f..7c9412760e 100644 --- a/app/assets/javascripts/templates/active_selector.html.haml +++ b/app/assets/javascripts/templates/active_selector.html.haml @@ -1,2 +1,2 @@ -%li{"ng-class" => "{active: selector.active}"} - %a{"ng-transclude" => true} +%li{ ng: { class: "{active: selector.active}" } } + %a{ ng: { transclude: true, class: "{active: selector.active}" } } diff --git a/app/assets/javascripts/templates/filter_selector.html.haml b/app/assets/javascripts/templates/filter_selector.html.haml new file mode 100644 index 0000000000..cddcd2f523 --- /dev/null +++ b/app/assets/javascripts/templates/filter_selector.html.haml @@ -0,0 +1,3 @@ +%active-selector{ ng: { repeat: "selector in selectors()", show: "ifDefined(selector.fits, true)" } } + %render-svg{path: "{{selector.object.icon}}", ng: { if: "selector.object.icon"} } + %span {{ selector.object.name }} diff --git a/app/assets/javascripts/templates/partials/enterprise_details.html.haml b/app/assets/javascripts/templates/partials/enterprise_details.html.haml index 90741a5a8c..3a9d97f59e 100644 --- a/app/assets/javascripts/templates/partials/enterprise_details.html.haml +++ b/app/assets/javascripts/templates/partials/enterprise_details.html.haml @@ -1,9 +1,28 @@ -.row{bindonce: true} +.row{bindonce: true} .small-12.large-8.columns - %div{"ng-if" => "enterprise.long_description.length > 0 || enterprise.logo"} + / TODO: Rob add logic for taxons and properties too: + / %div{"ng-if" => "enterprise.long_description.length > 0 || enterprise.logo"} + %div %p.modal-header About - .about-container - %img.enterprise-logo{"bo-src" => "enterprise.logo", "bo-if" => "enterprise.logo"} + / TODO: Rob - add in taxons and properties and property pop-overs + + -# TODO: Add producer taxons and properties here + -# %div + -# %span.filter-shopfront.taxon-selectors + -# %ul.inline-block + -# %li + -# %a.button.tiny.disabled Grains + -# %li + -# %a.button.tiny.disabled Dairy + -# + -# %span.filter-shopfront.property-selectors.pad-top + -# %ul.inline-block + -# %li + -# %a.button.tiny Organic certified + -# / TODO: Rob - need popover, use will's directive or this? http://pineconellc.github.io/angular-foundation/ + -# + .about-container.pad-top + %img.enterprise-logo{"bo-src" => "enterprise.logo", "bo-if" => "enterprise.logo"} %p.text-small{"ng-bind-html" => "enterprise.long_description"} .small-12.large-4.columns %ng-include{src: "'partials/contact.html'"} diff --git a/app/assets/javascripts/templates/product_modal.html.haml b/app/assets/javascripts/templates/product_modal.html.haml index 82cda0a371..1b59838e53 100644 --- a/app/assets/javascripts/templates/product_modal.html.haml +++ b/app/assets/javascripts/templates/product_modal.html.haml @@ -1,9 +1,37 @@ .row + + .columns.small-12.large-6.product-header + %h3 {{product.name}} + %span + %em from + %span.avenir {{ enterprise.name }} + + + -# TODO: Add product taxons and properties here + -# / TODO: Rob - add in taxons and properties and property pop-overs + -# / %render-svg{path: "{{product.primary_taxon.icon}}"} + -# .pad-top + -# %span.filter-shopfront.taxon-selectors + -# %ul.inline-block + -# %li + -# %a.button.tiny.disabled Grains + -# %li + -# %a.button.tiny.disabled Dairy + -# + -# %span.filter-shopfront.property-selectors.pad-top + -# %ul.inline-block + -# %li + -# %a.button.tiny Organic certified + -# / TODO: Rob - need popover, use will's directive or this? http://pineconellc.github.io/angular-foundation/ + + + %div{"ng-if" => "product.description"} + %hr + %p.text-small {{product.description}} + %hr + .columns.small-12.large-6 %img.product-img{"ng-src" => "{{product.largeImage}}", "ng-if" => "product.largeImage"} - .columns.small-12.large-6.product-header - %h2 - / %render-svg{path: "{{product.primary_taxon.icon}}"} - {{product.name}} - %p {{product.description}} + %img.product-img.placeholder{"ng-src" => "/assets/noimage/large.png", "ng-if" => "!product.largeImage"} + %ng-include{src: "'partials/close.html'"} diff --git a/app/assets/javascripts/templates/registration/about.html.haml b/app/assets/javascripts/templates/registration/about.html.haml index 5b7d2e6df6..be9948b95d 100644 --- a/app/assets/javascripts/templates/registration/about.html.haml +++ b/app/assets/javascripts/templates/registration/about.html.haml @@ -40,6 +40,16 @@ .field %label{ for: 'enterprise_acn' } ACN: %input.chunky{ id: 'enterprise_acn', placeholder: "eg. 123 456 789", ng: { model: 'enterprise.acn' } } + .row + .small-12.columns + .field + %label{ for: 'enterprise_charges_sales_tax' }= t(:charges_sales_tax) + %input{ id: 'enterprise_charges_sales_tax_true', type: 'radio', name: 'charges_sales_tax', value: 'true', required: true, ng: { model: 'enterprise.charges_sales_tax' } } + %label{ for: 'enterprise_charges_sales_tax_true' } Yes + %input{ id: 'enterprise_charges_sales_tax_false', type: 'radio', name: 'charges_sales_tax', value: 'false', required: true, ng: { model: 'enterprise.charges_sales_tax' } } + %label{ for: 'enterprise_charges_sales_tax_false' } No + %span.error.small-12.columns{ ng: { show: "about.charges_sales_tax.$error.required && submitted" } } + You need to make a selection. .row.buttons.pad-top .small-12.columns diff --git a/app/assets/javascripts/templates/single_line_selectors.html.haml b/app/assets/javascripts/templates/single_line_selectors.html.haml new file mode 100644 index 0000000000..e1e96e904a --- /dev/null +++ b/app/assets/javascripts/templates/single_line_selectors.html.haml @@ -0,0 +1,14 @@ +%ul + -# In order for the single-line-selector scope to have access to the available selectors, + %filter-selector{objects: "objects()", "active-selectors" => "activeSelectors", "all-selectors" => "allSelectors" } + + %li.more{ ng: { show: "overFlowSelectors().length > 0 || fitting" } } + %a.dropdown{ data: { dropdown: "show-more" }, ng: { class: "{active: selectedOverFlowSelectors().length > 0}" } } + %span + + {{ overFlowSelectors().length }} more + %i.ofn-i_052-point-down + .f-dropdown.text-right.content#show-more + %ul + %active-selector{ ng: { repeat: "selector in overFlowSelectors()", hide: "selector.fits" } } + %render-svg{path: "{{selector.object.icon}}"} + %span {{ selector.object.name }} diff --git a/app/assets/javascripts/templates/taxon_selector.html.haml b/app/assets/javascripts/templates/taxon_selector.html.haml deleted file mode 100644 index 02450c8d12..0000000000 --- a/app/assets/javascripts/templates/taxon_selector.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -%active-selector{"ng-repeat" => "selector in selectors()"} - %render-svg{path: "{{selector.taxon.icon}}"} - %span {{ selector.taxon.name }} diff --git a/app/assets/stylesheets/darkswarm/_shop-filters.css.sass b/app/assets/stylesheets/darkswarm/_shop-filters.css.sass new file mode 100644 index 0000000000..0d59f260f4 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/_shop-filters.css.sass @@ -0,0 +1,122 @@ +@import mixins +@import branding +@import big-input +@import animations + +@mixin filter-selector($base-clr, $border-clr, $hover-clr) + ul.inline-block + display: inline-block + + li + display: inline-block + @include border-radius(0) + padding: 0 + margin: 0 0 0.25rem 0.25rem + &:hover, &:focus + background: transparent + &.active + box-shadow: none + + a, a.button + display: block + padding-top: 0.5rem + @include border-radius(0.5em) + border: 1px solid $border-clr + padding: 0.5em 0.625em + font-size: 0.875em + color: $base-clr + font-size: 0.75em + background: white + margin: 0 + i + padding-left: 0.25rem + + render-svg + &, & svg + width: 1rem + height: 1rem + float: left + padding-right: 0.25rem + path + @include csstrans + fill: $base-clr + + &:hover, &:focus + border-color: $hover-clr + color: $hover-clr + render-svg + svg + path + fill: $hover-clr + + &.disabled + opacity: 0.6 + + &:hover, &:focus + border-color: $border-clr + color: $base-clr + render-svg + svg + path + fill: $base-clr + + + &.active, &.active:hover, &.active:focus + border: 1px solid $base-clr + background: $base-clr + color: white + render-svg + svg + path + fill: white + + +// Alert when search, taxon, filter is triggered + +.alert-box.search-alert + background-color: $clr-yellow-light + border-color: $clr-yellow-light + color: #777 + font-size: 0.75rem + padding: 0.5rem 0.75rem + + span.applied-properties + color: #333 + + span.applied-taxons + color: $clr-blue + + span.applied-search + color: $clr-brick + + span.filter-label + opacity: 0.75 + +.filter-shopfront.taxon-selectors, .filter-shopfront.property-selectors + background: transparent + + single-line-selectors + overflow-x: hidden + white-space: nowrap + + .f-dropdown + overflow-x: auto + white-space: normal + + ul + margin: 0 + ul, ul li + list-style: none + + +.filter-shopfront + + // Shopfront taxons + &.taxon-selectors + @include filter-selector($clr-blue, $clr-blue-light, $clr-blue-bright) + + // Shopfront properties + &.property-selectors + @include filter-selector(#666, #ccc, #777) + + diff --git a/app/assets/stylesheets/darkswarm/_shop-inputs.css.sass b/app/assets/stylesheets/darkswarm/_shop-inputs.css.sass index f11c31a153..b191913020 100644 --- a/app/assets/stylesheets/darkswarm/_shop-inputs.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-inputs.css.sass @@ -9,8 +9,7 @@ @include placeholder(rgba(0,0,0,0.4), #777) input#search - @include big-input(rgba(0,0,0,0.3), #777, $clr-brick) - @include big-input-static + @include medium-input(rgba(0,0,0,0.3), #777, $clr-brick) // ordering product diff --git a/app/assets/stylesheets/darkswarm/_shop-modals.css.sass b/app/assets/stylesheets/darkswarm/_shop-modals.css.sass new file mode 100644 index 0000000000..405c1bc701 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/_shop-modals.css.sass @@ -0,0 +1,6 @@ + +.product-header + h1, h2, h3, h4, h5, h6 + margin: 0 + hr + margin: 0.5em 0 \ No newline at end of file diff --git a/app/assets/stylesheets/darkswarm/active_table_search.css.sass b/app/assets/stylesheets/darkswarm/active_table_search.css.sass index 635d2e2e4b..49981895d0 100644 --- a/app/assets/stylesheets/darkswarm/active_table_search.css.sass +++ b/app/assets/stylesheets/darkswarm/active_table_search.css.sass @@ -8,7 +8,7 @@ margin-left: 0 margin-right: 0 -.row.filter-box:first-child, .row.filter-box.filter-box-shopfront +.row.filter-box:first-child border: 1px solid $clr-blue-light @include border-radius(0.25em) margin-top: 2px @@ -19,13 +19,9 @@ background: transparent margin-top: 1em -.row.filter-box.filter-box-shopfront - margin-top: 0 - products .filter-box background: #f7f7f7 - .filter-box background: rgba(245,245,245,0.6) .tdhead @@ -40,7 +36,6 @@ products .filter-box [class*="block-grid-"] > li padding-bottom: 0.5rem !important - li @include border-radius(12px) padding-top: 0.5rem @@ -105,16 +100,6 @@ products .filter-box path fill: #666 -.filter-box.filter-box-shopfront - .tdhead - margin-top: 0rem - margin-bottom: 0.75rem - padding: 0.5rem 0 - h5 - color: $clr-blue - .button.tiny - margin-bottom: 0rem - .button.filterbtn margin-bottom: 0 !important min-width: 160px diff --git a/app/assets/stylesheets/darkswarm/animations.sass b/app/assets/stylesheets/darkswarm/animations.sass index 544e2823ce..62e87e7224 100644 --- a/app/assets/stylesheets/darkswarm/animations.sass +++ b/app/assets/stylesheets/darkswarm/animations.sass @@ -2,6 +2,8 @@ // ANIMATION FUNCTIONS + +// @-webkit-keyframes slideInDown 0% opacity: 0 @@ -22,6 +24,8 @@ -ms-transform: translateY(0) transform: translateY(0) + + @-webkit-keyframes slideOutUp 0% -webkit-transform: translateY(0) @@ -160,6 +164,51 @@ product.animate-repeat -webkit-animation-fill-mode: both animation-fill-mode: both +// + +.animate-slide + max-height: 500px + opacity: 1 !important + -webkit-transition: all 300ms ease-in-out + -moz-transition: all 300ms ease-in-out + -o-transition: all 300ms ease-in-out + transition: all 300ms ease-in-out + + &.ng-hide + overflow: hidden + max-height: 0 + opacity: 0 !important + + // &.ng-hide-add-active, &.ng-hide-remove-active + + &.ng-hide-add, &.ng-hide-remove + /* IMPORTANT: this needs to be here to make it visible during the animation + since the .ng-hide class is already on the element rendering + it as hidden. */ + display: block !important + + +.animate-show + opacity: 1 !important + -webkit-transition: all 300ms ease-in-out + -moz-transition: all 300ms ease-in-out + -o-transition: all 300ms ease-in-out + transition: all 300ms ease-in-out + + &.ng-hide + opacity: 0 !important + + // &.ng-hide-add-active, &.ng-hide-remove-active + + &.ng-hide-add, &.ng-hide-remove + /* IMPORTANT: this needs to be here to make it visible during the animation + since the .ng-hide class is already on the element rendering + it as hidden. */ + display: block !important + + + + @mixin csstrans -webkit-transition: all 300ms ease diff --git a/app/assets/stylesheets/darkswarm/big-input.sass b/app/assets/stylesheets/darkswarm/big-input.sass index 15bf937f39..165404fff3 100644 --- a/app/assets/stylesheets/darkswarm/big-input.sass +++ b/app/assets/stylesheets/darkswarm/big-input.sass @@ -13,7 +13,7 @@ border: 2px solid $input font-size: 2rem box-shadow: 0 - padding: 0.75rem 1rem 0.35rem 1rem + padding: 0.75rem 1rem 0.35rem height: auto width: 100% margin-bottom: 0.5rem @@ -33,8 +33,9 @@ background: white background: rgba(255,255,255,0.5) text-shadow: 0 0 10px #ffffff - padding: 1.5rem 1rem 1rem 1rem + padding: 1.5rem 1rem 1rem letter-spacing: 0.02rem + outline: none @mixin big-input-static outline: 0 @@ -42,6 +43,34 @@ padding: 0.75rem 1rem 0.35rem 1rem letter-spacing: 0 +@mixin medium-input($input, $inputhvr, $inputactv) + @include avenir + @include csstrans + @include border-radius(0.5rem) + background: rgba(255,255,255,0.1) + border: 2px solid $input + font-size: 0.875rem + box-shadow: 0 + padding: 0.5rem 0.625rem 0.375rem + height: auto + width: 100% + margin-bottom: 0.5rem + box-shadow: none + color: $inputactv + + &:hover + @include box-shadow(0 1px 1px 0 rgba(255,255,255,0.25)) + border: 2px solid $inputhvr + color: $inputactv + + &:active, &:focus, &.active + border: 2px solid $inputactv + color: $inputactv + background: white + background: rgba(255,255,255,0.5) + text-shadow: 0 0 10px #ffffff + outline: none + @mixin placeholder($placeholder, $placeholderhvr) ::-webkit-input-placeholder color: $placeholder diff --git a/app/assets/stylesheets/darkswarm/branding.css.sass b/app/assets/stylesheets/darkswarm/branding.css.sass index e476f54baf..da2600818e 100644 --- a/app/assets/stylesheets/darkswarm/branding.css.sass +++ b/app/assets/stylesheets/darkswarm/branding.css.sass @@ -15,6 +15,8 @@ $clr-blue: #0096ad $clr-blue-light: #85d9e5 $clr-blue-bright: #14b6cc +$clr-yellow-light: #faf6c7 + $disabled-light: #e5e5e5 $disabled-bright: #ccc $disabled-med: #b3b3b3 @@ -25,3 +27,4 @@ $med-drk-grey: #444 $dark-grey: #333 $light-grey: #ddd $black: #000 + diff --git a/app/assets/stylesheets/darkswarm/images.css.sass b/app/assets/stylesheets/darkswarm/images.css.sass index 798e656481..b89cbb21fc 100644 --- a/app/assets/stylesheets/darkswarm/images.css.sass +++ b/app/assets/stylesheets/darkswarm/images.css.sass @@ -7,6 +7,11 @@ margin-bottom: 10px outline: 1px solid #ccc @include box-shadow(0 1px 2px 1px rgba(0,0,0,0.15)) + // placeholder for when no product images + &.placeholder + opacity: 0.35 + @media all and (max-width: 1024px) + display: none .hero-img outline: 1px solid $disabled-bright diff --git a/app/assets/stylesheets/darkswarm/modals.css.sass b/app/assets/stylesheets/darkswarm/modals.css.sass index 578a3ceccb..560153c5b1 100644 --- a/app/assets/stylesheets/darkswarm/modals.css.sass +++ b/app/assets/stylesheets/darkswarm/modals.css.sass @@ -8,6 +8,7 @@ dialog, .reveal-modal border-bottom: 30px solid white overflow-y: scroll overflow-x: hidden + min-height: 260px // Not working yet - want a nice gradient shadow when there is overflow - needs JS too // &:after // @include elipse-shadow(0 0 40px rgba(0, 0, 0, 0.8)) @@ -32,6 +33,7 @@ dialog, .reveal-modal .reveal-modal-bg background-color: rgba(0,0,0,0.85) + position: fixed dialog .close-reveal-modal, .reveal-modal .close-reveal-modal right: 0.25rem diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 89557fbbec..9d38398f36 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -15,10 +15,8 @@ products display: block - padding-top: 2.3em + padding-top: 20px @media all and (max-width: 768px) - padding-top: 1em - input.button.right float: left diff --git a/app/assets/stylesheets/darkswarm/shopping-cart.css.sass b/app/assets/stylesheets/darkswarm/shopping-cart.css.sass index 6dbbe103e6..cb71913831 100644 --- a/app/assets/stylesheets/darkswarm/shopping-cart.css.sass +++ b/app/assets/stylesheets/darkswarm/shopping-cart.css.sass @@ -20,21 +20,27 @@ right: 22px !important left: auto - ul, li - list-style: none - margin-left: 0 + table + width: 100% + border: none + border-spacing: 0px + margin-bottom: 5px - li - float: none - - .row .columns - padding-left: 0.25rem - padding-right: 0.25rem - - li.total-cart - background-color: #424242 - li.product-cart - border-top: 1px solid #424242 + tr.total-cart + color: #fff + background-color: #424242 + td + color: #fff + tr.product-cart + background-color: #333333 + border-top: 1px solid #424242 + td + padding: 4px 12px + color: #fff + .buttons + .button + height: auto + top: 0px // Shopping cart #cart-detail diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e96a0f316e..53763ad274 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -8,15 +8,12 @@ class ApplicationController < ActionController::Base super(options, response_status) end - def after_sign_in_path_for(resource) + def set_checkout_redirect if request.referer and referer_path = URI(request.referer).path - [main_app.checkout_path].include?(referer_path) ? referer_path : root_path - else - root_path + session["spree_user_return_to"] = [main_app.checkout_path].include?(referer_path) ? referer_path : root_path end end - private def require_distributor_chosen diff --git a/app/controllers/enterprise_confirmations_controller.rb b/app/controllers/enterprise_confirmations_controller.rb index b9868c6f98..d275260599 100644 --- a/app/controllers/enterprise_confirmations_controller.rb +++ b/app/controllers/enterprise_confirmations_controller.rb @@ -32,6 +32,24 @@ class EnterpriseConfirmationsController < DeviseController set_flash_message(:error, :not_confirmed) if is_navigational_format? end - respond_with_navigational(resource){ redirect_to spree.admin_path } + respond_with_navigational(resource){ redirect_to redirect_path(resource) } end -end \ No newline at end of file + + private + + def new_user_reset_path(resource) + password = Devise.friendly_token.first(8) + user = Spree::User.create(email: resource.email, password: password, password_confirmation: password) + user.send_reset_password_instructions + resource.users << user + spree.edit_spree_user_password_path(user, :reset_password_token => user.reset_password_token, return_to: spree.admin_path) + end + + def redirect_path(resource) + if resource.persisted? && !Spree::User.exists?(email: resource.email) + new_user_reset_path(resource) + else + spree.admin_path + end + end +end diff --git a/app/controllers/spree/user_sessions_controller_decorator.rb b/app/controllers/spree/user_sessions_controller_decorator.rb index c0d5d72381..72dc8b6d14 100644 --- a/app/controllers/spree/user_sessions_controller_decorator.rb +++ b/app/controllers/spree/user_sessions_controller_decorator.rb @@ -1,4 +1,6 @@ Spree::UserSessionsController.class_eval do + before_filter :set_checkout_redirect, only: :create + def create authenticate_spree_user! diff --git a/app/controllers/user_passwords_controller.rb b/app/controllers/user_passwords_controller.rb index ed3be07f35..1870cc8859 100644 --- a/app/controllers/user_passwords_controller.rb +++ b/app/controllers/user_passwords_controller.rb @@ -1,6 +1,8 @@ class UserPasswordsController < Spree::UserPasswordsController layout 'darkswarm' + before_filter :set_admin_redirect, only: :edit + def create self.resource = resource_class.send_reset_password_instructions(params[resource_name]) @@ -18,4 +20,10 @@ class UserPasswordsController < Spree::UserPasswordsController end end end + + private + + def set_admin_redirect + session["spree_user_return_to"] = params[:return_to] if params[:return_to] + end end diff --git a/app/controllers/user_registrations_controller.rb b/app/controllers/user_registrations_controller.rb index a6a0074553..5728de6672 100644 --- a/app/controllers/user_registrations_controller.rb +++ b/app/controllers/user_registrations_controller.rb @@ -1,4 +1,5 @@ class UserRegistrationsController < Spree::UserRegistrationsController + before_filter :set_checkout_redirect, only: :create # POST /resource/sign_up def create diff --git a/app/helpers/checkout_helper.rb b/app/helpers/checkout_helper.rb index cd986eb565..c1139b63f9 100644 --- a/app/helpers/checkout_helper.rb +++ b/app/helpers/checkout_helper.rb @@ -18,20 +18,24 @@ module CheckoutHelper end def display_checkout_admin_and_handling_adjustments_total_for(order) - adjustments = order.adjustments.eligible.where('originator_type = ? AND source_type != ? ', 'EnterpriseFee', 'Spree::LineItem' ) - Spree::Money.new( adjustments.sum( &:amount ) , { :currency => order.currency }) + adjustments = order.adjustments.eligible.where('originator_type = ? AND source_type != ? ', 'EnterpriseFee', 'Spree::LineItem') + Spree::Money.new adjustments.sum(&:amount) , currency: order.currency end def checkout_line_item_adjustments(order) - order.adjustments.eligible.where( source_type: "Spree::LineItem") + order.adjustments.eligible.where(source_type: "Spree::LineItem") end def checkout_subtotal(order) - order.item_total + checkout_line_item_adjustments(order).sum( &:amount ) + order.item_total + checkout_line_item_adjustments(order).sum(&:amount) end def display_checkout_subtotal(order) - Spree::Money.new( checkout_subtotal(order) , { :currency => order.currency }) + Spree::Money.new checkout_subtotal(order) , currency: order.currency + end + + def display_checkout_tax_total(order) + Spree::Money.new order.total_tax, currency: order.currency end def checkout_state_options(source_address) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index a168d0284d..37794cef9d 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -21,6 +21,10 @@ module InjectionHelper inject_json_ams "taxons", Spree::Taxon.all, Api::TaxonSerializer end + def inject_properties + inject_json_ams "properties", Spree::Property.all, Api::IdNameSerializer + end + def inject_currency_config inject_json_ams "currencyConfig", {}, Api::CurrencyConfigSerializer end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 97766078b1..9b81405489 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -77,7 +77,7 @@ class Enterprise < ActiveRecord::Base after_rollback :restore_permalink scope :by_name, order('name') - scope :visible, where(:visible => true) + scope :visible, where(visible: true) scope :confirmed, where('confirmed_at IS NOT NULL') scope :unconfirmed, where('confirmed_at IS NULL') scope :activated, where("confirmed_at IS NOT NULL AND sells != 'unspecified'") @@ -320,6 +320,11 @@ class Enterprise < ActiveRecord::Base end end + # Based on a devise method, but without adding errors + def pending_any_confirmation? + !confirmed? || pending_reconfirmation? + end + protected def devise_mailer diff --git a/app/models/spree/shipment_decorator.rb b/app/models/spree/shipment_decorator.rb index ee9189efdd..460fae4985 100644 --- a/app/models/spree/shipment_decorator.rb +++ b/app/models/spree/shipment_decorator.rb @@ -3,7 +3,11 @@ module Spree def ensure_correct_adjustment_with_included_tax ensure_correct_adjustment_without_included_tax - adjustment.set_included_tax! Config.shipping_tax_rate if Config.shipment_inc_vat + if Config.shipment_inc_vat && (order.distributor.nil? || order.distributor.charges_sales_tax) + adjustment.set_included_tax! Config.shipping_tax_rate + else + adjustment.set_included_tax! 0 + end end alias_method_chain :ensure_correct_adjustment, :included_tax diff --git a/app/models/spree/tax_rate_decorator.rb b/app/models/spree/tax_rate_decorator.rb index 835c001a05..42eae86b8c 100644 --- a/app/models/spree/tax_rate_decorator.rb +++ b/app/models/spree/tax_rate_decorator.rb @@ -1,4 +1,13 @@ Spree::TaxRate.class_eval do + class << self + def match_with_sales_tax_registration(order) + return [] if order.distributor && !order.distributor.charges_sales_tax + match_without_sales_tax_registration(order) + end + alias_method_chain :match, :sales_tax_registration + end + + def adjust_with_included_tax(order) adjust_without_included_tax(order) @@ -7,6 +16,5 @@ Spree::TaxRate.class_eval do a.set_absolute_included_tax! a.amount end end - alias_method_chain :adjust, :included_tax end diff --git a/app/serializers/api/id_name_serializer.rb b/app/serializers/api/id_name_serializer.rb new file mode 100644 index 0000000000..1db5f2439e --- /dev/null +++ b/app/serializers/api/id_name_serializer.rb @@ -0,0 +1,3 @@ +class Api::IdNameSerializer < ActiveModel::Serializer + attributes :id, :name +end diff --git a/app/serializers/api/product_serializer.rb b/app/serializers/api/product_serializer.rb index 0026290b6d..78f7593e4f 100644 --- a/app/serializers/api/product_serializer.rb +++ b/app/serializers/api/product_serializer.rb @@ -35,7 +35,7 @@ class Api::CachedProductSerializer < ActiveModel::Serializer has_many :variants, serializer: Api::VariantSerializer has_many :taxons, serializer: Api::IdSerializer - has_many :properties, serializer: Api::PropertySerializer + has_many :properties, serializer: Api::IdSerializer has_many :images, serializer: Api::ImageSerializer has_one :supplier, serializer: Api::IdSerializer diff --git a/app/views/admin/enterprises/form/_business_details.html.haml b/app/views/admin/enterprises/form/_business_details.html.haml index 7fb4d2ccb3..92f727d8d3 100644 --- a/app/views/admin/enterprises/form/_business_details.html.haml +++ b/app/views/admin/enterprises/form/_business_details.html.haml @@ -3,8 +3,21 @@ = f.label :abn, 'ABN' .omega.eight.columns = f.text_field :abn, { placeholder: "eg. 99 123 456 789"} + .row .alpha.three.columns = f.label :acn, 'ACN' .omega.eight.columns - = f.text_field :acn, { placeholder: "eg. 123 456 789"} \ No newline at end of file + = f.text_field :acn, { placeholder: "eg. 123 456 789"} + +.row + .three.columns.alpha + %label= t('charges_sales_tax') + .two.columns + = f.radio_button :charges_sales_tax, true +   + = f.label :charges_sales_tax, "Yes", :value => "true" + .five.columns.omega + = f.radio_button :charges_sales_tax, false +   + = f.label :charges_sales_tax, "No", :value => "false" diff --git a/app/views/admin/enterprises/form/_contact.html.haml b/app/views/admin/enterprises/form/_contact.html.haml index c16f93f4b0..df28b6a921 100644 --- a/app/views/admin/enterprises/form/_contact.html.haml +++ b/app/views/admin/enterprises/form/_contact.html.haml @@ -1,9 +1,10 @@ --if @enterprise.unconfirmed_email +-if @enterprise.pending_any_confirmation? .alert-box - Email change is pending. + - email = @enterprise.confirmed? ? @enterprise.unconfirmed_email : @enterprise.email + Email confirmation is pending. We've sent a confirmation email to - %strong= "#{@enterprise.unconfirmed_email}." - = link_to('Resend', main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: @enterprise.unconfirmed_email } ), method: :post) + %strong= "#{email}." + = link_to('Resend', main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: email } ), method: :post) %a.close{ href: "#" } × .row .alpha.three.columns @@ -30,4 +31,4 @@ .alpha.three.columns = f.label :website .omega.eight.columns - = f.text_field :website, { placeholder: "eg. www.truffles.com"} \ No newline at end of file + = f.text_field :website, { placeholder: "eg. www.truffles.com"} diff --git a/app/views/checkout/_summary.html.haml b/app/views/checkout/_summary.html.haml index 5bdd13187b..1d24d1dcf2 100644 --- a/app/views/checkout/_summary.html.haml +++ b/app/views/checkout/_summary.html.haml @@ -19,11 +19,6 @@ %tr %th Total %td.total.text-right {{ Checkout.cartTotal() | localizeCurrency }} - - if current_order.price_adjustment_totals.present? - - current_order.price_adjustment_totals.each do |label, total| - %tr - %th= label - %td= total //= f.submit "Purchase", class: "button", "ofn-focus" => "accordion['payment']" %a.button.secondary{href: cart_url} diff --git a/app/views/home/_filters.html.haml b/app/views/home/_filters.html.haml index 52fceb6688..7fcda4bbf4 100644 --- a/app/views/home/_filters.html.haml +++ b/app/views/home/_filters.html.haml @@ -10,8 +10,7 @@ .light Filter by Type %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-5 - %taxon-selector{objects: "Enterprises.hubs | searchEnterprises:query", - results: "activeTaxons"} + %filter-selector{objects: "Enterprises.hubs | searchEnterprises:query | taxonsOf", "active-selectors" => "activeTaxons"} .small-12.large-3.columns %h5.tdhead .light Filter by diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 0c6ec608d4..e1976ab167 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -28,6 +28,7 @@ = inject_json "user", "current_user" = inject_json "railsFlash", "flash" = inject_taxons + = inject_properties = inject_current_order = inject_currency_config @@ -37,6 +38,6 @@ %section{ role: "main" } = yield - + #footer %loading diff --git a/app/views/producers/_filters.html.haml b/app/views/producers/_filters.html.haml index e1cd0ccba9..b548bd90c4 100644 --- a/app/views/producers/_filters.html.haml +++ b/app/views/producers/_filters.html.haml @@ -11,7 +11,5 @@ .light Filter by Type %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-6 - %taxon-selector{objects: "Enterprises.producers | searchEnterprises:query ", - results: "activeTaxons"} + %filter-selector{objects: "Enterprises.producers | searchEnterprises:query | taxonsOf", "active-selectors" => "activeTaxons"} = render partial: 'shared/components/filter_box' - diff --git a/app/views/shared/menu/_cart.html.haml b/app/views/shared/menu/_cart.html.haml index 716d198f67..f8287c8f61 100644 --- a/app/views/shared/menu/_cart.html.haml +++ b/app/views/shared/menu/_cart.html.haml @@ -10,38 +10,33 @@ %span.joyride-nub.top .joyride-content-wrapper %h5 Your shopping cart - %ul - %li.product-cart{"ng-repeat" => "line_item in Cart.line_items_present()", + %table + %tr.product-cart{"ng-repeat" => "line_item in Cart.line_items_present()", "ng-controller" => "LineItemCtrl", "id" => "cart-variant-{{ line_item.variant.id }}"} - .row - .columns.small-7 - %small - / %strong {{ line_item.variant.name_to_display }} - / %em {{ line_item.variant.unit_to_display }} - / - if {{ line_item.product.name }} == {{ line_item.variant.name_to_display }} - %strong - {{ line_item.variant.extended_name }} + %td + %small + %strong + {{ line_item.variant.extended_name }} + %td.text-right + %small + %span.quantity {{ line_item.quantity }} + %i.ofn-i_009-close + %span.price {{ line_item.variant.price_with_fees | localizeCurrency }} - .columns.small-3.text-right - %small - %span.quantity {{ line_item.quantity }} - %i.ofn-i_009-close - %span.price {{ line_item.variant.price_with_fees | localizeCurrency }} + %td + %small + \= + %strong + .total-price.right {{ line_item.variant.totalPrice() | localizeCurrency }} - .columns.small-2 - %small - \= - %strong - .total-price.right {{ line_item.variant.totalPrice() | localizeCurrency }} + %table{"ng-show" => "Cart.line_items_present().length > 0"} + %tr.total-cart + %td + %em Total: + %td.text-right + %strong {{ Cart.total() | 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() | localizeCurrency }} - - .text-right + .buttons.text-right %a.button.secondary.tiny.add_to_cart{ href: cart_path, type: :submit, "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" } {{ Cart.dirty ? 'Updating cart...' : (Cart.empty() ? 'Cart empty' : 'Edit your cart' ) }} %a.button.primary.tiny{href: checkout_path, "ng-disabled" => "Cart.dirty || Cart.empty()"} Checkout now diff --git a/app/views/shop/products/_filters.html.haml b/app/views/shop/products/_filters.html.haml index 395e6f123d..786cd48662 100644 --- a/app/views/shop/products/_filters.html.haml +++ b/app/views/shop/products/_filters.html.haml @@ -1,21 +1,5 @@ -.row.animate-show{"ng-hide" => "filtersActive"} - .small-12.columns - = render partial: 'shared/components/filter_controls_shopfront' - -.row.filter-box.filter-box-shopfront.animate-hide{"ng-show" => "filtersActive"} - .small-12.columns - .row.tdhead - .small-12.medium-6.columns - %h5 - .light Filter by - Category - .small-12.medium-6.columns.text-right - = render partial: 'shared/components/filter_box_shopfront' - = render partial: 'shared/components/filter_controls_shopfront' - - .row - .small-12.columns - %ul.small-block-grid-2.medium-block-grid-3.large-block-grid-4 - %taxon-selector{objects: "Products.products | products:query | products:showProfiles", - results: "activeTaxons"} +.filter-shopfront.taxon-selectors.text-right + %single-line-selectors{ objects: "Products.products | products:query | properties: activeProperties | taxonsOf", "active-selectors" => "activeTaxons"} +.filter-shopfront.property-selectors.text-right + %single-line-selectors{ objects: "Products.products | products:query | taxons:activeTaxons | propertiesOf", "active-selectors" => "activeProperties"} diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index e247d7e3f2..0df4f022aa 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -1,24 +1,36 @@ %products.small-12.columns{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null", "infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1"} + // TODO: Needs an ng-show to slide content down + .row.animate-slide{ "ng-show" => "query || appliedPropertiesList() || appliedTaxonsList()" } + .small-12.columns + .alert-box.search-alert.ng-scope + %a.right{"ng-click" => "clearAll()"} + Clear all + %i.ofn-i_009-close + %span.filter-label + Showing: + %span.applied-properties + {{ appliedPropertiesList() }} + %span.applied-taxons + {{ appliedTaxonsList() }} + %span{ ng: { hide: "!query"} } + %span{ "ng-show" => "appliedPropertiesList() || appliedTaxonsList()" } + with + %span.applied-search "{{ query }}" .row - .small-12.medium-8.large-9.columns + .small-12.medium-6.large-5.columns %input#search.text{"ng-model" => "query", placeholder: "Search by product or producer", "ng-debounce" => "100", "ofn-disable-enter" => true} + .small-12.medium-6.large-6.large-offset-1.columns = render partial: "shop/products/filters" - %form{action: cart_path} - .small-12.medium-4.large-3.columns - %i.ofn-i_011-spinner.cart-spinner{"ng-show" => "Cart.dirty"} - %input.small.button.primary.right.add_to_cart{type: :submit, value: "{{ Cart.dirty ? 'Updating cart...' : (Cart.empty() ? 'Cart empty' : 'Edit your cart' ) }}", "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" } - %div.pad-top{bindonce: true} %product.animate-repeat{"ng-controller" => "ProductNodeCtrl", - "ng-repeat" => "product in filteredProducts = (Products.products | products:query | taxons:activeTaxons) track by product.id ", "id" => "product-{{ product.id }}"} - + "ng-repeat" => "product in filteredProducts = (Products.products | products:query | taxons:activeTaxons | properties: activeProperties) track by product.id ", "id" => "product-{{ product.id }}"} = render partial: "shop/products/summary" %shop-variant{variant: 'product.master', "bo-if" => "!product.hasVariants", "id" => "variant-{{ product.master.id }}"} %shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants track by variant.id", "id" => "variant-{{ variant.id }}"} @@ -43,4 +55,3 @@ %form{action: cart_path} %i.ofn-i_011-spinner.cart-spinner{"ng-show" => "Cart.dirty"} %input.small.button.primary.right.add_to_cart{type: :submit, value: "{{ Cart.dirty ? 'Updating cart...' : (Cart.empty() ? 'Cart empty' : 'Edit your cart' ) }}", "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" } - diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 90ecc663ea..3641e0cbd5 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -2,10 +2,10 @@ .row %tabset -# Build all tabs. - - for name, heading_cols in { about: ["About #{current_distributor.name}", 4], - producers: ["Producers",3], - groups: ["Groups",2], - contact: ["Contact",3]} + - for name, heading_cols in { about: ["About #{current_distributor.name}", 6], + producers: ["Producers",2], + contact: ["Contact",2], + groups: ["Groups",2]} -# tabs take tab path in 'active' and 'select' functions defined in TabsCtrl. - heading, cols = heading_cols %tab.columns{heading: heading, diff --git a/app/views/spree/order_mailer/_order_summary.html.haml b/app/views/spree/order_mailer/_order_summary.html.haml new file mode 100644 index 0000000000..86b1af1f3b --- /dev/null +++ b/app/views/spree/order_mailer/_order_summary.html.haml @@ -0,0 +1,53 @@ +%table.order-summary{:width => "100%"} + %thead + %tr + %th{:align => "left"} + %h4 Item + %th{:align => "right", :width => "25%"} + %h4 Qty + %th{:align => "right", :width => "25%"} + %h4 Price + %tbody + - @order.line_items.each do |item| + %tr + %td + - if item.variant.product.name == item.variant.name_to_display + %strong= "#{raw(item.variant.product.name)}" + - else + %strong + %span= "#{raw(item.variant.product.name)}" + %span= "- " + "#{raw(item.variant.name_to_display)}" + - if item.variant.options_text + = "(" + "#{raw(item.variant.options_text)}" + ")" + %br + %small + %em= raw(item.variant.product.supplier.name) + %td{:align => "right"} + = item.quantity + %td{:align => "right"} + = item.display_amount_with_adjustments + %tfoot + %tr + %td{:align => "right", :colspan => "2"} + Subtotal: + %td{:align => "right"} + = display_checkout_subtotal(@order) + - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| + %tr + %td{:align => "right", :colspan => "2"} + = "#{raw(adjustment.label)}:" + %td{:align => "right"} + = adjustment.display_amount + %tr + %td{:align => "right", :colspan => "2"} + %strong Total: + %td{:align => "right"} + %strong= @order.display_total + + - if @order.total_tax > 0 + %tr + %td{:align => "right", :colspan => "2"} + (includes tax): + %td{:align => "right"} + = display_checkout_tax_total(@order) +%p   diff --git a/app/views/spree/order_mailer/_payment.html.haml b/app/views/spree/order_mailer/_payment.html.haml new file mode 100644 index 0000000000..41968ba1ad --- /dev/null +++ b/app/views/spree/order_mailer/_payment.html.haml @@ -0,0 +1,14 @@ +- if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description + %p.callout + %span{:style => "float:right;"} + - if @order.paid? + PAID + - else + NOT PAID + %strong Payment summary + %h4 + Paying via: + %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe + %p + %em= @order.payments.first.andand.payment_method.andand.description.andand.html_safe + %p   diff --git a/app/views/spree/order_mailer/_shipping.html.haml b/app/views/spree/order_mailer/_shipping.html.haml new file mode 100644 index 0000000000..cb9cde72ca --- /dev/null +++ b/app/views/spree/order_mailer/_shipping.html.haml @@ -0,0 +1,59 @@ +- if @order.shipping_method.andand.require_ship_address + / Delivery details + %p.callout + %strong + - if @order.shipping_method.andand.name + #{@order.shipping_method.name.html_safe} + - else + Delivery details + + - if @order.order_cycle.andand.pickup_time_for(@order.distributor) + %h4 + Delivery on: + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + - if @order.shipping_method.andand.description + %p + %em #{@order.shipping_method.description.html_safe} + %br   + + - if @order.ship_address + %h4 Delivery address: + %p + #{@order.ship_address.full_name} + %br + #{@order.ship_address.full_address} + %br + #{@order.ship_address.phone} + %br   + + +- else + / Collection details + %p.callout + %strong + - if @order.shipping_method.andand.name + #{@order.shipping_method.name.html_safe} + - else + Collection details + + - if @order.order_cycle.andand.pickup_time_for(@order.distributor).present? + %h4 + Ready for collection: + %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} + + - if @order.shipping_method.andand.description.present? + %p + %em #{@order.shipping_method.description.html_safe} + %br   + + - if @order.ship_address.full_address + %p + %strong Collecting from: + %br + #{@order.ship_address.full_address} + + - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor).present? + %p + %strong Collection instructions: + %br + #{@order.order_cycle.pickup_instructions_for(@order.distributor)} diff --git a/app/views/spree/order_mailer/_special_instructions.html.haml b/app/views/spree/order_mailer/_special_instructions.html.haml new file mode 100644 index 0000000000..5145260411 --- /dev/null +++ b/app/views/spree/order_mailer/_special_instructions.html.haml @@ -0,0 +1,7 @@ +- if @order.special_instructions.present? + %br + %p + %small + %strong Your notes: + %br + #{@order.special_instructions} diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 4488c76edb..0816bd432d 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -23,135 +23,10 @@ Here are your order details from %strong= "#{@order.distributor.name}:" -%table.order-summary{:width => "100%"} - %thead - %tr - %th{:align => "left"} - %h4 Item - %th{:align => "right", :width => "25%"} - %h4 Qty - %th{:align => "right", :width => "25%"} - %h4 Price - %tbody - - @order.line_items.each do |item| - %tr - %td - - if item.variant.product.name == item.variant.name_to_display - %strong= "#{raw(item.variant.product.name)}" - - else - %strong - %span= "#{raw(item.variant.product.name)}" - %span= "- " + "#{raw(item.variant.name_to_display)}" - - if item.variant.options_text - = "(" + "#{raw(item.variant.options_text)}" + ")" - %br - %small - %em= raw(item.variant.product.supplier.name) - %td{:align => "right"} - = item.quantity - %td{:align => "right"} - = item.display_amount_with_adjustments - %tfoot - %tr - %td{:align => "right", :colspan => "2"} - Subtotal: - %td{:align => "right"} - = display_checkout_subtotal(@order) - - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| - %tr - %td{:align => "right", :colspan => "2"} - = "#{raw(adjustment.label)}:" - %td{:align => "right"} - = adjustment.display_amount - %tr - %td{:align => "right", :colspan => "2"} - %strong Total: - %td{:align => "right"} - %strong= @order.display_total -%p   - -- if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description - %p.callout - %span{:style => "float:right;"} - - if @order.paid? - PAID - - else - NOT PAID - %strong Payment summary - %h4 - Paying via: - %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe - %p - %em= @order.payments.first.andand.payment_method.andand.description.andand.html_safe - %p   - -- if @order.shipping_method.andand.require_ship_address - / Delivery details - %p.callout - %strong - - if @order.shipping_method.andand.name - #{@order.shipping_method.name.html_safe} - - else - Delivery details - - - if @order.order_cycle.andand.pickup_time_for(@order.distributor) - %h4 - Delivery on: - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - - if @order.shipping_method.andand.description - %p - %em #{@order.shipping_method.description.html_safe} - %br   - - - if @order.ship_address - %h4 Delivery address: - %p - #{@order.ship_address.full_name} - %br - #{@order.ship_address.full_address} - %br - #{@order.ship_address.phone} - %br   - - -- else - / Collection details - %p.callout - %strong - - if @order.shipping_method.andand.name - #{@order.shipping_method.name.html_safe} - - else - Collection details - - - if @order.order_cycle.andand.pickup_time_for(@order.distributor).present? - %h4 - Ready for collection: - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - - - if @order.shipping_method.andand.description.present? - %p - %em #{@order.shipping_method.description.html_safe} - %br   - - - if @order.ship_address.full_address - %p - %strong Collecting from: - %br - #{@order.ship_address.full_address} - - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor).present? - %p - %strong Collection instructions: - %br - #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - -- if @order.special_instructions.present? - %br - %p - %small - %strong Your notes: - %br - #{@order.special_instructions} += render 'order_summary' += render 'payment' += render 'shipping' += render 'special_instructions' %br %p.callout diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index fe48c1c6d1..f62b3cdfb4 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -23,134 +23,10 @@ %strong= "#{@order.bill_address.firstname} #{@order.bill_address.lastname}" completed the following order at your shopfront: -%table.order-summary{:width => "100%"} - %thead - %tr - %th{:align => "left"} - %h4 Item - %th{:align => "right", :width => "25%"} - %h4 Qty - %th{:align => "right", :width => "25%"} - %h4 Price - %tbody - - @order.line_items.each do |item| - %tr - %td - - if item.variant.product.name == item.variant.name_to_display - %strong= "#{raw(item.variant.product.name)}" - - else - %strong - %span= "#{raw(item.variant.product.name)}" - %span= "- " + "#{raw(item.variant.name_to_display)}" - - if item.variant.options_text - = "(" + "#{raw(item.variant.options_text)}" + ")" - %br - %small - %em= raw(item.variant.product.supplier.name) - %td{:align => "right"} - = item.quantity - %td{:align => "right"} - = item.display_amount_with_adjustments - %tfoot - %tr - %td{:align => "right", :colspan => "2"} - Subtotal: - %td{:align => "right"} - = display_checkout_subtotal(@order) - - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| - %tr - %td{:align => "right", :colspan => "2"} - = "#{raw(adjustment.label)}:" - %td{:align => "right"} - = adjustment.display_amount - %tr - %td{:align => "right", :colspan => "2"} - %strong Total: - %td{:align => "right"} - %strong= @order.display_total -%p   - -- if @order.payments.first.andand.payment_method.andand.type == "Spree::PaymentMethod::Check" and @order.payments.first.andand.payment_method.andand.description - %p.callout - %span{:style => "float:right;"} - - if @order.paid? - PAID - - else - NOT PAID - %strong Payment summary - %h4 - Paying via: - %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe - %p - %em= @order.payments.first.andand.payment_method.andand.description.andand.html_safe - %p   - -- if @order.shipping_method.andand.require_ship_address - / Delivery details - %p.callout - %strong - - if @order.shipping_method.andand.name - #{@order.shipping_method.name.html_safe} - - else - Delivery details - - - if @order.order_cycle.andand.pickup_time_for(@order.distributor) - %h4 - Delivery on: - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - - if @order.shipping_method.andand.description - %p - %em #{@order.shipping_method.description.html_safe} - %br   - - - if @order.ship_address - %h4 Delivery address: - %p - #{@order.ship_address.full_name} - %br - #{@order.ship_address.full_address} - %br - #{@order.ship_address.phone} - %br   - -- else - / Collection details - %p.callout - %strong - - if @order.shipping_method.andand.name - #{@order.shipping_method.name.html_safe} - - else - Collection details - - - if @order.order_cycle.andand.pickup_time_for(@order.distributor).present? - %h4 - Ready for collection: - %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - - - if @order.shipping_method.andand.description.present? - %p - %em #{@order.shipping_method.description.html_safe} - %br   - - - if @order.ship_address.full_address - %p - %strong Collecting from: - %br - #{@order.ship_address.full_address} - - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor).present? - %p - %strong Collection instructions: - %br - #{@order.order_cycle.pickup_instructions_for(@order.distributor)} - -- if @order.special_instructions.present? - %br - %p - %small - %strong Customer notes: - %br - #{@order.special_instructions} += render 'order_summary' += render 'payment' += render 'shipping' += render 'special_instructions' %p   = render 'shared/mailers/signoff' diff --git a/app/views/spree/orders/_form.html.haml b/app/views/spree/orders/_form.html.haml index bba45a53c8..e71f92452f 100644 --- a/app/views/spree/orders/_form.html.haml +++ b/app/views/spree/orders/_form.html.haml @@ -56,3 +56,10 @@ %td.text-right %h5.order-total.grand-total= @order.display_total %td + + - if @order.total_tax > 0 + %tr + %td.text-right{colspan:"3"} (includes tax) + %td.text-right + %span.order-total.tax-total= display_checkout_tax_total(@order) + %td diff --git a/app/views/spree/orders/show.html.haml b/app/views/spree/orders/show.html.haml index f9d2830486..82120add82 100644 --- a/app/views/spree/orders/show.html.haml +++ b/app/views/spree/orders/show.html.haml @@ -9,7 +9,7 @@ - else = @order.distributor.next_collection_at - = render partial: "shopping_shared/details" + = render "shopping_shared/details" %fieldset#order_summary{"data-hook" => ""} .row @@ -22,7 +22,7 @@ - if params.has_key? :checkout_complete %h1= t(:thank_you_for_your_order) - = render :partial => 'spree/shared/order_details', :locals => { :order => @order } + = render 'spree/shared/order_details', order: @order .row .columns.large-12 diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index da7bc84ca1..4fe07f07aa 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -130,37 +130,36 @@ %td.text-right.total{"data-hook" => "order_item_total"} %span= item.display_amount_with_adjustments.to_html - %tfoot#order-total{"data-hook" => "order_details_total"} - %tr.total - %td.text-right{colspan: "3"} - %h5 - Total - %td.text-right.total - %h5#order_total= order.display_total.to_html - - - if order.price_adjustment_totals.present? - %tfoot#price-adjustments{"data-hook" => "order_details_price_adjustments"} - - order.price_adjustment_totals.each do |key, total| - %tr.total - %td.text-right{colspan: "3"} - %strong - = key - %td.text-right.total - %span= total - - %tfoot#subtotal{"data-hook" => "order_details_subtotal"} - %tr#subtotal-row.total - %td.text-right{colspan: "3"} - %strong - Produce - %td.text-right.total - %span= display_checkout_subtotal(order) - - %tfoot#order-charges{"data-hook" => "order_details_adjustments"} - - checkout_adjustments_for(order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| - %tr.total - %td.text-right{:colspan => "3"} + %tfoot + #subtotal{"data-hook" => "order_details_subtotal"} + %tr#subtotal-row.total + %td.text-right{colspan: "3"} %strong - = adjustment.label + Produce %td.text-right.total - %span= adjustment.display_amount.to_html + %span= display_checkout_subtotal(order) + + #order-charges{"data-hook" => "order_details_adjustments"} + - checkout_adjustments_for(order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| + %tr.total + %td.text-right{:colspan => "3"} + %strong + = adjustment.label + %td.text-right.total + %span= adjustment.display_amount.to_html + + #order-total{"data-hook" => "order_details_total"} + %tr.total + %td.text-right{colspan: "3"} + %h5 + Total + %td.text-right.total + %h5#order_total= order.display_total.to_html + + - if order.total_tax > 0 + #tax{"data-hook" => "order_details_tax"} + %tr#tax-row.total + %td.text-right{colspan: "3"} + (includes tax) + %td.text-right.total + %span= display_checkout_tax_total(order) diff --git a/config/initializers/delayed_job.rb b/config/initializers/delayed_job.rb new file mode 100644 index 0000000000..b95d718c6d --- /dev/null +++ b/config/initializers/delayed_job.rb @@ -0,0 +1 @@ +Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'delayed_job.log')) diff --git a/config/initializers/js_template_helpers.rb b/config/initializers/js_template_helpers.rb new file mode 100644 index 0000000000..27fffc9655 --- /dev/null +++ b/config/initializers/js_template_helpers.rb @@ -0,0 +1,13 @@ +# Make helpers (#t in particular) available to javascript templates +# https://github.com/pitr/angular-rails-templates/issues/45#issuecomment-43229086 + +Rails.application.assets.context_class.class_eval do + # include ApplicationHelper + # include ActionView::Helpers + # include Rails.application.routes.url_helpers + + # Including all of the helpers (above) has caused some intermittent CSS include issues + # (not finding mixins from an @include in sass). Therefore, we're only including the + # bare minimum here. + include ActionView::Helpers::TranslationHelper +end diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 41a02f2de2..50fbe8a6db 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -21,3 +21,4 @@ en-GB: search_by_name: Search by name... producers: UK Producers producers_join: UK producers are now welcome to join Open Food Network UK. + charges_sales_tax: Charges sales tax? diff --git a/config/locales/en.yml b/config/locales/en.yml index fe5bafa19f..fe7d3c53aa 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -18,3 +18,4 @@ en: search_by_name: Search by name or suburb... producers: Aussie Producers producers_join: Australian producers are now welcome to join the Open Food Network. + charges_sales_tax: Charges GST? diff --git a/db/migrate/20150407234739_add_charges_sales_tax_to_enterprises.rb b/db/migrate/20150407234739_add_charges_sales_tax_to_enterprises.rb new file mode 100644 index 0000000000..ba8dfa7a08 --- /dev/null +++ b/db/migrate/20150407234739_add_charges_sales_tax_to_enterprises.rb @@ -0,0 +1,5 @@ +class AddChargesSalesTaxToEnterprises < ActiveRecord::Migration + def change + add_column :enterprises, :charges_sales_tax, :boolean, null: false, default: false + end +end diff --git a/script/delayed_job.sh b/script/delayed_job.sh new file mode 100644 index 0000000000..a1498d3ce6 --- /dev/null +++ b/script/delayed_job.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +export HOME="/home/openfoodweb" +export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH" + +$HOME/apps/openfoodweb/current/script/delayed_job $@ diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 75a0dc1a4b..79f72cfac3 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -272,24 +272,19 @@ module Admin context "setting 'sells' to 'own'" do before do - enterprise.sells = 'own' + enterprise.sells = 'none' enterprise.save! end context "if the trial has finished" do - before do - enterprise.shop_trial_start_date = (Date.today - 30.days).to_time - enterprise.save! - end - it "is disallowed" do Timecop.freeze(Time.zone.local(2015, 4, 16, 14, 0, 0)) do + enterprise.update_attribute(:shop_trial_start_date, 30.days.ago.beginning_of_day) spree_post :set_sells, { id: enterprise, sells: 'own' } expect(response).to redirect_to spree.admin_path trial_expiry = Date.today.strftime("%Y-%m-%d") expect(flash[:error]).to eq "Sorry, but you've already had a trial. Expired on: #{trial_expiry}" - expect(enterprise.reload.sells).to eq 'own' - expect(enterprise.reload.shop_trial_start_date).to eq (Date.today - 30.days).to_time + expect(enterprise.reload.sells).to eq 'none' end end end diff --git a/spec/controllers/enterprise_confirmations_controller_spec.rb b/spec/controllers/enterprise_confirmations_controller_spec.rb index fa7746df36..6fdab61be2 100644 --- a/spec/controllers/enterprise_confirmations_controller_spec.rb +++ b/spec/controllers/enterprise_confirmations_controller_spec.rb @@ -4,25 +4,58 @@ describe EnterpriseConfirmationsController do include AuthenticationWorkflow let!(:user) { create_enterprise_user( enterprise_limit: 10 ) } let!(:unconfirmed_enterprise) { create(:distributor_enterprise, confirmed_at: nil, owner: user) } - let!(:confirmed_enterprise) { create(:distributor_enterprise, owner: user) } + let!(:confirmed_enterprise) { create(:distributor_enterprise, confirmed_at: nil, owner: user) } + let!(:confirmed_token) { confirmed_enterprise.confirmation_token } let!(:unowned_enterprise) { create(:distributor_enterprise) } before do controller.stub spree_current_user: user @request.env["devise.mapping"] = Devise.mappings[:enterprise] + confirmed_enterprise.confirm! end context "confirming an enterprise" do - it "that has already been confirmed" do - spree_get :show, confirmation_token: confirmed_enterprise.confirmation_token - expect(response).to redirect_to spree.admin_path - expect(flash[:error]).to eq I18n.t('devise.enterprise_confirmations.enterprise.not_confirmed') + context "that has already been confirmed" do + + before do + spree_get :show, confirmation_token: confirmed_token + end + + it "redirects the user to admin" do + expect(response).to redirect_to spree.admin_path + expect(flash[:error]).to eq I18n.t('devise.enterprise_confirmations.enterprise.not_confirmed') + end end - it "that has not already been confirmed" do - spree_get :show, confirmation_token: unconfirmed_enterprise.confirmation_token - expect(response).to redirect_to spree.admin_path - expect(flash[:success]).to eq I18n.t('devise.enterprise_confirmations.enterprise.confirmed') + context "that has not been confirmed" do + context "where the enterprise contact email maps to an existing user account" do + before do + unconfirmed_enterprise.update_attribute(:email, user.email) + end + + it "redirects the user to admin" do + spree_get :show, confirmation_token: unconfirmed_enterprise.confirmation_token + expect(response).to redirect_to spree.admin_path + expect(flash[:success]).to eq I18n.t('devise.enterprise_confirmations.enterprise.confirmed') + end + end + + context "where the enterprise contact email doesn't map to an existing user account" do + let(:new_user) { create_enterprise_user } + + before do + unconfirmed_enterprise.update_attribute(:email, 'random@email.com') + allow(Spree::User).to receive(:create) { new_user } + allow(new_user).to receive(:reset_password_token) { "token" } + end + + it "redirects to the user to reset their password" do + spree_get :show, confirmation_token: unconfirmed_enterprise.confirmation_token + expect(response).to redirect_to spree.edit_spree_user_password_path(new_user, :reset_password_token => "token", return_to: spree.admin_path) + expect(flash[:success]).to eq I18n.t('devise.enterprise_confirmations.enterprise.confirmed') + expect(unconfirmed_enterprise.users(:reload)).to include new_user + end + end end end @@ -39,4 +72,4 @@ describe EnterpriseConfirmationsController do expect(flash[:error]).to eq "Authorization Failure" end end -end \ No newline at end of file +end diff --git a/spec/controllers/spree/user_sessions_controller_spec.rb b/spec/controllers/spree/user_sessions_controller_spec.rb new file mode 100644 index 0000000000..b4235db82b --- /dev/null +++ b/spec/controllers/spree/user_sessions_controller_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Spree::UserSessionsController do + include AuthenticationWorkflow + + let(:user) { create_enterprise_user } + + before do + @request.env["devise.mapping"] = Devise.mappings[:spree_user] + end + + describe "create" do + context "succeed" do + context "when referer is not '/checkout'" do + it "redirects to root" do + spree_post :create, spree_user: {email: user.email, password: user.password }, :use_route => :spree + response.should redirect_to root_path + end + end + + context "when referer is '/checkout'" do + before { @request.env['HTTP_REFERER'] = 'http://test.com/checkout' } + + it "redirects to checkout" do + spree_post :create, spree_user: { email: user.email, password: user.password }, :use_route => :spree + response.should redirect_to checkout_path + end + end + end + end +end diff --git a/spec/controllers/user_passwords_controller_spec.rb b/spec/controllers/user_passwords_controller_spec.rb index 25c285e09b..686d46656e 100644 --- a/spec/controllers/user_passwords_controller_spec.rb +++ b/spec/controllers/user_passwords_controller_spec.rb @@ -9,15 +9,26 @@ describe UserPasswordsController do ActionMailer::Base.default_url_options[:host] = "test.host" end - it "returns errors" do - spree_post :create, spree_user: {} - response.should be_success - response.should render_template "spree/user_passwords/new" + describe "create" do + it "returns errors" do + spree_post :create, spree_user: {} + response.should be_success + response.should render_template "spree/user_passwords/new" + end + + it "redirects to login when data is valid" do + spree_post :create, spree_user: { email: user.email} + response.should be_redirect + end end - it "redirects to login when data is valid" do - spree_post :create, spree_user: { email: user.email} - response.should be_redirect + describe "edit" do + context "when given a redirect" do + it "stores the redirect path in 'spree_user_return_to'" do + spree_post :edit, reset_password_token: "token", return_to: "/return_path" + expect(session["spree_user_return_to"]).to eq "/return_path" + end + end end it "renders Darkswarm" do diff --git a/spec/controllers/user_registrations_controller_spec.rb b/spec/controllers/user_registrations_controller_spec.rb index ed47f0c059..baa4cb73c7 100644 --- a/spec/controllers/user_registrations_controller_spec.rb +++ b/spec/controllers/user_registrations_controller_spec.rb @@ -6,7 +6,7 @@ describe UserRegistrationsController do before do @request.env["devise.mapping"] = Devise.mappings[:spree_user] end - + describe "via ajax" do render_views it "returns errors when registration fails" do @@ -25,15 +25,31 @@ describe UserRegistrationsController do end end - it "renders new when registration fails" do - spree_post :create, spree_user: {} - response.status.should == 200 - response.should render_template "spree/user_registrations/new" + context "when registration fails" do + it "renders new" do + spree_post :create, spree_user: {} + response.status.should == 200 + response.should render_template "spree/user_registrations/new" + end end - it "redirects when registration succeeds" do - spree_post :create, spree_user: {email: "test@test.com", password: "testy123", password_confirmation: "testy123"}, :use_route => :spree - response.should be_redirect - assigns[:user].email.should == "test@test.com" + context "when registration succeeds" do + context "when referer is not '/checkout'" do + it "redirects to root" do + spree_post :create, spree_user: {email: "test@test.com", password: "testy123", password_confirmation: "testy123"}, :use_route => :spree + response.should redirect_to root_path + assigns[:user].email.should == "test@test.com" + end + end + + context "when referer is '/checkout'" do + before { @request.env['HTTP_REFERER'] = 'http://test.com/checkout' } + + it "redirects to checkout" do + spree_post :create, spree_user: {email: "test@test.com", password: "testy123", password_confirmation: "testy123"}, :use_route => :spree + response.should redirect_to checkout_path + assigns[:user].email.should == "test@test.com" + end + end end end diff --git a/spec/factories.rb b/spec/factories.rb index 82db4c1520..5de578c7a2 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -200,6 +200,7 @@ FactoryGirl.define do tax_category { create(:tax_category) } after(:create) do |product, proxy| + raise "taxed_product factory requires a zone" unless proxy.zone create(:tax_rate, amount: proxy.tax_rate_amount, tax_category: product.tax_category, included_in_price: true, calculator: Spree::Calculator::DefaultTax.new, zone: proxy.zone) end end diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index a75125bdb5..395f882b51 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -218,6 +218,7 @@ feature %q{ click_link "Business Details" fill_in 'enterprise_abn', :with => '09812309823' fill_in 'enterprise_acn', :with => '' + choose 'Yes' # enterprise_charges_sales_tax click_link "Address" fill_in 'enterprise_address_attributes_address1', :with => '35 Ballantyne St' @@ -237,6 +238,9 @@ feature %q{ @enterprise.reload expect(@enterprise.owner).to eq user + click_link "Business Details" + page.should have_checked_field "enterprise_charges_sales_tax_true" + click_link "Payment Methods" page.should have_checked_field "enterprise_payment_method_ids_#{payment_method.id}" diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 277f33593b..4149f2a6c1 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -107,10 +107,10 @@ feature %q{ page.should have_content 'Payment State' end - - describe "Sales tax report" do - let(:distributor1) { create(:distributor_enterprise, with_payment_and_shipping: true) } - let(:distributor2) { create(:distributor_enterprise, with_payment_and_shipping: true) } + + describe "sales tax report" do + let(:distributor1) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } + let(:distributor2) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } let(:user1) { create_enterprise_user enterprises: [distributor1] } let(:user2) { create_enterprise_user enterprises: [distributor2] } let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } @@ -140,7 +140,7 @@ feature %q{ click_link "Reports" click_link "Sales Tax" end - + it "reports" do # Then it should give me access only to managed enterprises page.should have_select 'q_distributor_id_eq', with_options: [user1.enterprises.first.name] diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb index 354a5c37eb..25a91ae160 100644 --- a/spec/features/consumer/registration_spec.rb +++ b/spec/features/consumer/registration_spec.rb @@ -60,15 +60,17 @@ feature "Registration", js: true do fill_in 'enterprise_long_desc', with: 'Long description' fill_in 'enterprise_abn', with: '12345' fill_in 'enterprise_acn', with: '54321' + choose 'Yes' # enterprise_charges_sales_tax click_button 'Continue' - # Enterprise should be update + # Enterprise should be updated expect(page).to have_content "Let's upload some pretty pictures so your profile looks great!" 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' + expect(e.charges_sales_tax).to be_true # Images # Move from logo page diff --git a/spec/features/consumer/shopping/cart_spec.rb b/spec/features/consumer/shopping/cart_spec.rb new file mode 100644 index 0000000000..7ec4c75004 --- /dev/null +++ b/spec/features/consumer/shopping/cart_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +feature "full-page cart", js: true do + include AuthenticationWorkflow + include WebHelper + include ShopWorkflow + include UIComponentHelper + + describe "viewing the cart" do + describe "tax" do + let!(:zone) { create(:zone_with_member) } + let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } + let(:supplier) { create(:supplier_enterprise) } + let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.master]) } + let(:enterprise_fee) { create(:enterprise_fee, amount: 11.00, tax_category: product.tax_category) } + let(:product) { create(:taxed_product, supplier: supplier, zone: zone, price: 110.00, tax_rate_amount: 0.1) } + let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } + + before do + add_enterprise_fee enterprise_fee + set_order order + add_product_to_cart + visit spree.cart_path + end + + it "shows the total tax for the order, including product tax and tax on fees" do + save_screenshot '/home/rohan/ss.png', full: true + page.should have_selector '.tax-total', text: '11.00' # 10 + 1 + end + end + end +end diff --git a/spec/features/consumer/shopping/checkout_auth_spec.rb b/spec/features/consumer/shopping/checkout_auth_spec.rb index aaf543a7fa..8b4f3bb977 100644 --- a/spec/features/consumer/shopping/checkout_auth_spec.rb +++ b/spec/features/consumer/shopping/checkout_auth_spec.rb @@ -9,7 +9,7 @@ feature "As a consumer I want to check out my cart", js: true do let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) } let(:supplier) { create(:supplier_enterprise) } - let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.master]) } let(:product) { create(:simple_product, supplier: supplier) } let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } let(:address) { create(:address, firstname: "Foo", lastname: "Bar") } diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 12eca09339..bf85db2a75 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -8,24 +8,23 @@ feature "As a consumer I want to check out my cart", js: true do include WebHelper include UIComponentHelper - let(:distributor) { create(:distributor_enterprise) } + let!(:zone) { create(:zone_with_member) } + let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:supplier) { create(:supplier_enterprise) } let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.master]) } - let(:enterprise_fee) { create(:enterprise_fee, amount: 1.23) } - let(:product) { create(:simple_product, supplier: supplier) } + let(:enterprise_fee) { create(:enterprise_fee, amount: 1.23, tax_category: product.tax_category) } + let(:product) { create(:taxed_product, supplier: supplier, price: 10, zone: zone, tax_rate_amount: 0.1) } let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } before do + Spree::Config.shipment_inc_vat = true + Spree::Config.shipping_tax_rate = 0.25 + add_enterprise_fee enterprise_fee set_order order add_product_to_cart end - it "shows the current distributor on checkout" do - visit checkout_path - page.should have_content distributor.name - end - describe "with shipping and payment methods" do 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)) } @@ -50,6 +49,11 @@ feature "As a consumer I want to check out my cart", js: true do checkout_as_guest end + it "shows the current distributor" do + visit checkout_path + page.should have_content distributor.name + end + it "shows a breakdown of the order price" do toggle_shipping choose sm2.name @@ -57,6 +61,11 @@ feature "As a consumer I want to check out my cart", js: true do page.should have_selector 'orderdetails .cart-total', text: "$11.23" page.should have_selector 'orderdetails .shipping', text: "$4.56" page.should have_selector 'orderdetails .total', text: "$15.79" + + # Tax should not be displayed in checkout, as the customer's choice of shipping method + # affects the tax and we haven't written code to live-update the tax amount when they + # make a change. + page.should_not have_content product.tax_category.name end it "shows all shipping methods, but doesn't show ship address when not needed" do @@ -117,14 +126,25 @@ feature "As a consumer I want to check out my cart", js: true do choose pm1.name end - expect do place_order page.should have_content "Your order has been processed successfully" end.to enqueue_job ConfirmOrderJob + # And the order's special instructions should be set o = Spree::Order.complete.first expect(o.special_instructions).to eq "SpEcIaL NoTeS" + + # And the Spree tax summary should not be displayed + page.should_not have_content product.tax_category.name + + # And the total tax for the order, including shipping and fee tax, should be displayed + # product tax ($10.00 @ 10% = $0.91) + # + fee tax ($ 1.23 @ 10% = $0.11) + # + shipping tax ($ 4.56 @ 25% = $0.91) + # = $1.93 + page.should have_content "(includes tax)" + page.should have_content "$1.93" end context "with basic details filled" do diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index e11664c457..248826ea5a 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -125,25 +125,6 @@ feature "As a consumer I want to shop with a distributor", js: true do let(:variant) { product.variants.first } let(:product2) { create(:simple_product, group_buy: false) } - describe "without variants" do - before do - add_product_to_order_cycle(exchange, product) - set_order_cycle(order, oc1) - visit shop_path - end - - # TODO move to controller test - pending "adding a product with a max quantity less than quantity results in max_quantity==quantity" do - fill_in "variants[#{variant.id}]", with: 5 - fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 1 - add_to_cart - page.should have_content product.name - li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last - li.max_quantity.should == 5 - li.quantity.should == 5 - end - end - describe "with variants on the product" do let(:variant) { create(:variant, product: product, on_hand: 10 ) } before do diff --git a/spec/helpers/checkout_helper_spec.rb b/spec/helpers/checkout_helper_spec.rb index c4979be40d..e47a5e8c51 100644 --- a/spec/helpers/checkout_helper_spec.rb +++ b/spec/helpers/checkout_helper_spec.rb @@ -12,4 +12,12 @@ describe CheckoutHelper do helper.validated_input("test", "foo", type: :email) end + + describe "displaying the tax total for an order" do + let(:order) { double(:order, total_tax: 123.45, currency: 'AUD') } + + it "retrieves the total tax on the order" do + helper.display_checkout_tax_total(order).should == Spree::Money.new(123.45, currency: 'AUD') + end + end end diff --git a/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee b/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee index 73ecd611ef..9e4237d1e7 100644 --- a/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee @@ -4,19 +4,25 @@ describe 'ProductsCtrl', -> event = null Products = null Cart = {} + Taxons = null + Properties = null beforeEach -> module('Darkswarm') - Products = + Products = all: -> update: -> products: ["testy mctest"] + loading: false OrderCycle = order_cycle: {} - - inject ($controller) -> - scope = {} - ctrl = $controller 'ProductsCtrl', {$scope: scope, Products: Products, OrderCycle: OrderCycle, Cart: Cart} + Taxons: + taxons: [] + Properties: {} + + inject ($rootScope, $controller) -> + scope = $rootScope + ctrl = $controller 'ProductsCtrl', {$scope: scope, Products: Products, OrderCycle: OrderCycle, Cart: Cart, Taxons: Taxons, Properties: Properties} it 'fetches products from Products', -> expect(scope.Products.products).toEqual ['testy mctest'] diff --git a/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee similarity index 74% rename from spec/javascripts/unit/darkswarm/services/product_spec.js.coffee rename to spec/javascripts/unit/darkswarm/services/products_spec.js.coffee index 27e3f5aff6..2cb23522e2 100644 --- a/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee @@ -4,13 +4,15 @@ describe 'Products service', -> Enterprises = null Variants = null Cart = null - CurrentHubMock = {} + CurrentHubMock = {} currentOrder = null product = null productWithImage = null + properties = null + taxons = null beforeEach -> - product = + product = test: "cats" supplier: id: 9 @@ -27,16 +29,23 @@ describe 'Products service', -> ] currentOrder = line_items: [] + properties = + { id: 1, name: "some property" } + taxons = + { id: 2, name: "some taxon" } module 'Darkswarm' module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock - $provide.value "currentOrder", currentOrder + $provide.value "CurrentHub", CurrentHubMock + $provide.value "currentOrder", currentOrder + $provide.value "taxons", taxons + $provide.value "properties", properties null inject ($injector, _$httpBackend_)-> Products = $injector.get("Products") Enterprises = $injector.get("Enterprises") + Properties = $injector.get("Properties") Variants = $injector.get("Variants") Cart = $injector.get("Cart") $httpBackend = _$httpBackend_ @@ -44,20 +53,32 @@ describe 'Products service', -> it "Fetches products from the backend on init", -> $httpBackend.expectGET("/shop/products").respond([product]) $httpBackend.flush() - expect(Products.products[0].test).toEqual "cats" + expect(Products.products[0].test).toEqual "cats" it "dereferences suppliers", -> - Enterprises.enterprises_by_id = + Enterprises.enterprises_by_id = {id: 9, name: "test"} $httpBackend.expectGET("/shop/products").respond([{supplier : {id: 9}, master: {}}]) $httpBackend.flush() expect(Products.products[0].supplier).toBe Enterprises.enterprises_by_id["9"] + it "dereferences taxons", -> + product.taxons = [2] + $httpBackend.expectGET("/shop/products").respond([product]) + $httpBackend.flush() + expect(Products.products[0].taxons[1]).toBe taxons[0] + + it "dereferences properties", -> + product.properties = [1] + $httpBackend.expectGET("/shop/products").respond([product]) + $httpBackend.flush() + expect(Products.products[0].properties[1]).toBe properties[0] + it "registers variants with Variants service", -> product.variants = [{id: 1}] $httpBackend.expectGET("/shop/products").respond([product]) $httpBackend.flush() - expect(Products.products[0].variants[0]).toBe Variants.variants[1] + expect(Products.products[0].variants[0]).toBe Variants.variants[1] it "registers variants with the Cart", -> product.variants = [{id: 8}] diff --git a/spec/javascripts/unit/darkswarm/services/properties_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/properties_spec.js.coffee new file mode 100644 index 0000000000..d40d4f8901 --- /dev/null +++ b/spec/javascripts/unit/darkswarm/services/properties_spec.js.coffee @@ -0,0 +1,16 @@ +describe "Properties service", -> + Properties = null + properties = [ + {id: 1, name: "Property1"} + {id: 2, name: "Property2"} + ] + + beforeEach -> + module('Darkswarm') + angular.module('Darkswarm').value 'properties', properties + + inject ($injector)-> + Properties = $injector.get("Properties") + + it "caches properties in an id-referenced hash", -> + expect(Properties.properties_by_id[1]).toBe properties[0] diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index e69d04da6f..579965aa7a 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -30,7 +30,8 @@ module Spree end describe "Shipment adjustments" do - let!(:order) { create(:order, shipping_method: shipping_method) } + let!(:order) { create(:order, distributor: hub, shipping_method: shipping_method) } + let(:hub) { create(:distributor_enterprise, charges_sales_tax: true) } let!(:line_item) { create(:line_item, order: order) } let(:shipping_method) { create(:shipping_method, calculator: Calculator::FlatRate.new(preferred_amount: 50.0)) } let(:adjustment) { order.adjustments(:reload).shipping.first } @@ -80,6 +81,13 @@ module Spree adjustment.included_tax.should == 0 end + + it "records 0% tax on shipments when the distributor does not charge sales tax" do + order.distributor.update_attributes! charges_sales_tax: false + order.reload.create_shipment! + + adjustment.included_tax.should == 0 + end end end @@ -88,7 +96,7 @@ module Spree let(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::DefaultTax.new, zone: zone, amount: 0.1) } let(:tax_category) { create(:tax_category, tax_rates: [tax_rate]) } - let(:coordinator) { create(:distributor_enterprise) } + let(:coordinator) { create(:distributor_enterprise, charges_sales_tax: true) } let(:variant) { create(:variant) } let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: [enterprise_fee], distributors: [coordinator], variants: [variant]) } let!(:order) { create(:order, order_cycle: order_cycle, distributor: coordinator) } diff --git a/spec/models/spree/tax_rate_spec.rb b/spec/models/spree/tax_rate_spec.rb new file mode 100644 index 0000000000..25aad986a0 --- /dev/null +++ b/spec/models/spree/tax_rate_spec.rb @@ -0,0 +1,33 @@ +module Spree + describe TaxRate do + describe "selecting tax rates to apply to an order" do + let!(:zone) { create(:zone_with_member) } + let!(:order) { create(:order, distributor: hub, bill_address: create(:address)) } + let!(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Calculator::FlatRate.new(preferred_amount: 0.1), zone: zone) } + + describe "when the order's hub charges sales tax" do + let(:hub) { create(:distributor_enterprise, charges_sales_tax: true) } + + it "selects all tax rates" do + TaxRate.match(order).should == [tax_rate] + end + end + + describe "when the order's hub does not charge sales tax" do + let(:hub) { create(:distributor_enterprise, charges_sales_tax: false) } + + it "selects no tax rates" do + TaxRate.match(order).should be_empty + end + end + + describe "when the order does not have a hub" do + let!(:order) { create(:order, distributor: nil, bill_address: create(:address)) } + + it "selects all tax rates" do + TaxRate.match(order).should == [tax_rate] + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d5410a5026..a911fe7742 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -33,7 +33,7 @@ require 'capybara/poltergeist' Capybara.javascript_driver = :poltergeist Capybara.register_driver :poltergeist do |app| - options = {phantomjs_options: ['--load-images=no'], window_size: [1280, 800]} + options = {phantomjs_options: ['--load-images=no'], window_size: [1280, 800], timeout: 1.minute} # Extend poltergeist's timeout to allow ample time to use pry in browser thread #options.merge! {timeout: 5.minutes} # Enable the remote inspector: Use page.driver.debug to open a remote debugger in chrome @@ -92,6 +92,7 @@ RSpec.configure do |config| config.include OpenFoodNetwork::FeatureToggleHelper config.include OpenFoodNetwork::EnterpriseGroupsHelper config.include OpenFoodNetwork::DistributionHelper + config.include OpenFoodNetwork::HtmlHelper config.include ActionView::Helpers::DateHelper config.include OpenFoodNetwork::DelayedJobHelper diff --git a/spec/support/html_helper.rb b/spec/support/html_helper.rb new file mode 100644 index 0000000000..54c6ffe1f1 --- /dev/null +++ b/spec/support/html_helper.rb @@ -0,0 +1,10 @@ +module OpenFoodNetwork + module HtmlHelper + def save_and_open(html) + require "launchy" + file = Tempfile.new('html') + file.write html + Launchy.open(file.path) + end + end +end diff --git a/spec/support/request/shop_workflow.rb b/spec/support/request/shop_workflow.rb index 8f088095e8..a0e6ab846c 100644 --- a/spec/support/request/shop_workflow.rb +++ b/spec/support/request/shop_workflow.rb @@ -17,11 +17,10 @@ module ShopWorkflow end def add_product_to_cart - create(:line_item, variant: product.master, order: order) - order.reload + populator = Spree::OrderPopulator.new(order, order.currency) + populator.populate(variants: {product.master.id => 1}) - # Recalculate totals - order.save! + # Recalculate fee totals order.update_distribution_charge! end