diff --git a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee index da595418da..8ed56f252d 100644 --- a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee @@ -6,7 +6,8 @@ Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Products, OrderCycle, $scope.filterText = FilterSelectorsService.filterText $scope.FilterSelectorsService = FilterSelectorsService $scope.limit = 3 - $scope.ordering = {order: "name"} + $scope.ordering = + order: "primary_taxon.name" $scope.order_cycle = OrderCycle.order_cycle $scope.incrementLimit = -> diff --git a/app/assets/javascripts/darkswarm/directives/active_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/active_selector.js.coffee index 74523c2c3d..563b5a1716 100644 --- a/app/assets/javascripts/darkswarm/directives/active_selector.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/active_selector.js.coffee @@ -1,4 +1,6 @@ Darkswarm.directive "activeSelector", -> + # A generic selector that allows an object/scope to be toggled between active and inactive + # Used in the filters, but hypothetically useable anywhere restrict: 'E' transclude: true replace: true @@ -8,5 +10,6 @@ Darkswarm.directive "activeSelector", -> elem.bind "click", -> scope.$apply -> scope.selector.active = !scope.selector.active - scope.emit() + # This function is a convention, e.g. a callback on the scope applied when active changes + scope.emit() if scope.emit diff --git a/app/assets/javascripts/darkswarm/directives/active_table_hub_link.js.coffee b/app/assets/javascripts/darkswarm/directives/active_table_hub_link.js.coffee index 4369fced34..994a7ced48 100644 --- a/app/assets/javascripts/darkswarm/directives/active_table_hub_link.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/active_table_hub_link.js.coffee @@ -1,11 +1,12 @@ Darkswarm.directive "activeTableHubLink", (CurrentHub, CurrentOrder) -> + # Change the text of the hub link based on CurrentHub + # To be used with ofnEmptiesCart + # Takes "change" and "shop" as text string attributes restrict: "A" scope: hub: '=activeTableHubLink' template: "{{action}}" link: (scope, elm, attr)-> - # Swap out the text of the hub link depending on whether it'll change current hub - # To be used with ofnEmptiesCart if CurrentHub.hub?.id and CurrentHub.hub.id isnt scope.hub.id scope.action = attr.change else diff --git a/app/assets/javascripts/darkswarm/directives/cart_popover.js.coffee b/app/assets/javascripts/darkswarm/directives/cart_popover.js.coffee index bcc0c8bc08..1ef1b3f9c3 100644 --- a/app/assets/javascripts/darkswarm/directives/cart_popover.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/cart_popover.js.coffee @@ -1,4 +1,5 @@ Darkswarm.directive "cart", -> + # Toggles visibility of the "cart" popover restrict: 'A' link: (scope, elem, attr)-> scope.open = false diff --git a/app/assets/javascripts/darkswarm/directives/debounce.js.coffee b/app/assets/javascripts/darkswarm/directives/debounce.js.coffee index 343fcb531a..80f55de1d7 100644 --- a/app/assets/javascripts/darkswarm/directives/debounce.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/debounce.js.coffee @@ -1,4 +1,6 @@ Darkswarm.directive "ngDebounce", ($timeout) -> + # Slows down ng-model updates, only triggering binding ngDebounce milliseconds + # after the last change. Used to prevent squirrely UI restrict: "A" require: "ngModel" priority: 99 diff --git a/app/assets/javascripts/darkswarm/directives/disable_enter.js.coffee b/app/assets/javascripts/darkswarm/directives/disable_enter.js.coffee index 352ed85fd6..ce1178e15e 100644 --- a/app/assets/javascripts/darkswarm/directives/disable_enter.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/disable_enter.js.coffee @@ -1,4 +1,5 @@ Darkswarm.directive "ofnDisableEnter", ()-> + # Stops enter from doing normal enter things restrict: 'A' link: (scope, element, attrs)-> element.bind "keydown keypress", (e)-> diff --git a/app/assets/javascripts/darkswarm/directives/disable_scroll.js.coffee b/app/assets/javascripts/darkswarm/directives/disable_scroll.js.coffee index 7c870ab860..40b8230e65 100644 --- a/app/assets/javascripts/darkswarm/directives/disable_scroll.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/disable_scroll.js.coffee @@ -1,6 +1,7 @@ Darkswarm.directive "ofnDisableScroll", ()-> + # Stops scrolling from incrementing or decrementing input value + # Useful for number inputs restrict: 'A' - link: (scope, element, attrs)-> element.bind 'focus', -> element.bind 'mousewheel', (e)-> diff --git a/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee b/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee index 75e88f3682..b49131afa6 100644 --- a/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee @@ -1,12 +1,13 @@ -Darkswarm.directive "ofnEmptiesCart", (CurrentHub, CurrentOrder, Navigation, storage) -> +Darkswarm.directive "ofnEmptiesCart", (CurrentHub, Cart, Navigation, storage) -> + # Compares scope.hub with CurrentHub. Will trigger an confirmation if they are different, + # and Cart isn't empty restrict: "A" + scope: + hub: "=ofnEmptiesCart" link: (scope, elm, attr)-> - hub = scope.$eval(attr.ofnEmptiesCart) - # A hub is selected, we're changing to a different hub, and the cart isn't empty - if CurrentHub.hub?.id and CurrentHub.hub.id isnt hub.id - unless CurrentOrder.empty() - elm.bind 'click', (ev)-> - ev.preventDefault() - if confirm "Are you sure? This will change your selected Hub and remove any items in you shopping cart." - storage.clearAll() # One day this will have to be moar GRANULAR - Navigation.go scope.hub.path + if CurrentHub.hub?.id and CurrentHub.hub.id isnt scope.hub.id and !Cart.empty() + elm.bind 'click', (ev)-> + ev.preventDefault() + if confirm "Are you sure? This will change your selected Hub and remove any items in you shopping cart." + storage.clearAll() # One day this will have to be moar GRANULAR + Navigation.go scope.hub.path diff --git a/app/assets/javascripts/darkswarm/directives/fill_vertical.js.coffee b/app/assets/javascripts/darkswarm/directives/fill_vertical.js.coffee index a8a2fbeebc..c9d05428ce 100644 --- a/app/assets/javascripts/darkswarm/directives/fill_vertical.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/fill_vertical.js.coffee @@ -1,10 +1,9 @@ Darkswarm.directive "fillVertical", ($window)-> + # Makes something fill the window vertically. Used on the Google Map. restrict: 'A' - link: (scope, element, attrs)-> setSize = -> element.css "height", ($window.innerHeight - element.offset().top) setSize() - angular.element($window).bind "resize", -> setSize() diff --git a/app/assets/javascripts/darkswarm/directives/flash.js.coffee b/app/assets/javascripts/darkswarm/directives/flash.js.coffee index b5c9aaddd8..74f6571f73 100644 --- a/app/assets/javascripts/darkswarm/directives/flash.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/flash.js.coffee @@ -1,5 +1,6 @@ Darkswarm.directive "ofnFlash", (flash, $timeout, RailsFlashLoader)-> - # Mappings between flash types (left) and Foundation classes + # Our own flash class. Uses the "flash" service (third party), and a directive + # called RailsFlashLoader to render typePairings = info: "info" error: "alert" @@ -13,6 +14,8 @@ Darkswarm.directive "ofnFlash", (flash, $timeout, RailsFlashLoader)-> link: ($scope, element, attr) -> $scope.flashes = [] + + # Callback when a new flash message is pushed to flash service show = (message, type)=> if message $scope.flashes.push({message: message, type: typePairings[type]}) @@ -21,5 +24,6 @@ Darkswarm.directive "ofnFlash", (flash, $timeout, RailsFlashLoader)-> $scope.delete = -> $scope.flashes.shift() + # Register our callback (above) with flash service flash.subscribe(show) RailsFlashLoader.initFlash() diff --git a/app/assets/javascripts/darkswarm/directives/focus.js.coffee b/app/assets/javascripts/darkswarm/directives/focus.js.coffee index c481702d6c..3c3bb4f05a 100644 --- a/app/assets/javascripts/darkswarm/directives/focus.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/focus.js.coffee @@ -1,9 +1,9 @@ Darkswarm.directive "ofnFocus", -> + # Takes an expression attrs.ofnFocus + # Watches value of expression, triggers element.focus() when value is truthy + # Used to automatically focus on specific inputs in various circumstances restrict: "A" link: (scope, element, attrs) -> scope.$watch attrs.ofnFocus, ((focus) -> focus and element.focus() - return ), true - - return diff --git a/app/assets/javascripts/darkswarm/directives/loading.js.coffee b/app/assets/javascripts/darkswarm/directives/loading.js.coffee index febe56de9b..c7191b4fe2 100644 --- a/app/assets/javascripts/darkswarm/directives/loading.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/loading.js.coffee @@ -1,4 +1,5 @@ Darkswarm.directive "loading", (Loading)-> + # Triggers a screen-wide "loading" thing when Ajaxy stuff is happening scope: {} restrict: 'E' templateUrl: 'loading.html' @@ -6,5 +7,3 @@ Darkswarm.directive "loading", (Loading)-> $scope.Loading = Loading $scope.show = -> $scope.Loading.message? - - link: ($scope, element, attr)-> diff --git a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee index 4942032580..88456db2a3 100644 --- a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee @@ -3,7 +3,7 @@ Darkswarm.directive 'mapSearch', ($timeout)-> restrict: 'E' require: '^googleMap' replace: true - template: '' + template: '' link: (scope, elem, attrs, ctrl)-> $timeout => map = ctrl.getMap() diff --git a/app/assets/javascripts/darkswarm/directives/max.js.coffee b/app/assets/javascripts/darkswarm/directives/max.js.coffee new file mode 100644 index 0000000000..ed3d7b3253 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/max.js.coffee @@ -0,0 +1,6 @@ +Darkswarm.directive "max", -> + restrict: 'A' + link: (scope, elem, attr)-> + elem.bind 'input', -> + if elem.val() > +attr.max + elem.val attr.max diff --git a/app/assets/javascripts/darkswarm/directives/modal.js.coffee b/app/assets/javascripts/darkswarm/directives/modal.js.coffee index d727fa64c9..7c1babe215 100644 --- a/app/assets/javascripts/darkswarm/directives/modal.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/modal.js.coffee @@ -1,16 +1,19 @@ Darkswarm.directive "ofnModal", ($modal)-> + # Generic modal! Uses transclusion so designer-types can do stuff like: + # %ofn-modal + # CONTENT + # Only works for simple cases, so roll your own when necessary! restrict: 'E' replace: true transclude: true - scope: {} + scope: true template: "{{title}}" + # Instead of using ng-transclude we compile the transcluded template to a string + # This compiled template is sent to the $modal service! Such magic! + # In theory we could compile the template directly inside link rather than onclick, but it's performant so meh! link: (scope, elem, attrs, ctrl, transclude)-> scope.title = attrs.title - contents = null elem.on "click", => - # We're using an isolate scope, which is a child of the original scope - # We have to compile the transclude against the original scope, not the isolate - transclude scope.$parent, (clone)-> - contents = clone - scope.modalInstance = $modal.open(controller: ctrl, template: contents, scope: scope.$parent) + transclude scope, (clone)-> + scope.modalInstance = $modal.open(controller: ctrl, template: clone, scope: scope) diff --git a/app/assets/javascripts/darkswarm/directives/price_breakdown.js.coffee b/app/assets/javascripts/darkswarm/directives/price_breakdown.js.coffee index 4875cf81ec..8b0beb17a5 100644 --- a/app/assets/javascripts/darkswarm/directives/price_breakdown.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/price_breakdown.js.coffee @@ -1,11 +1,20 @@ Darkswarm.directive "priceBreakdown", ($tooltip)-> - tooltip = $tooltip 'priceBreakdown', 'priceBreakdown', 'click' + # We use the $tooltip service from Angular foundation to give us boilerplate + # Subsequently we patch the scope, template and restrictions + tooltip = $tooltip 'priceBreakdown', 'priceBreakdown', 'click' tooltip.scope = variant: "=" + tooltip.templateUrl = "price_breakdown_button.html" + tooltip.replace = true + tooltip.restrict = 'E' tooltip +# This is automatically referenced via naming convention in $tooltip Darkswarm.directive 'priceBreakdownPopup', -> restrict: 'EA' replace: true templateUrl: 'price_breakdown.html' - scope: true + scope: false + + link: (scope, elem, attrs) -> + scope.expanded = false unless scope.expanded? diff --git a/app/assets/javascripts/darkswarm/directives/price_percentage.js.coffee b/app/assets/javascripts/darkswarm/directives/price_percentage.js.coffee new file mode 100644 index 0000000000..35140598c4 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/price_percentage.js.coffee @@ -0,0 +1,10 @@ +Darkswarm.directive "pricePercentage", -> + restrict: 'E' + replace: true + templateUrl: 'price_percentage.html' + scope: + percentage: '=' + + link: (scope, elem, attrs) -> + elem.find(".meter").css + width: "#{scope.percentage}%" diff --git a/app/assets/javascripts/darkswarm/directives/render_svg.js.coffee b/app/assets/javascripts/darkswarm/directives/render_svg.js.coffee index 86ffea8c5b..c53448473d 100644 --- a/app/assets/javascripts/darkswarm/directives/render_svg.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/render_svg.js.coffee @@ -1,7 +1,11 @@ Darkswarm.directive "renderSvg", ()-> + # Magical directive that'll render SVGs from URLs + # If only there were a neater way of doing this restrict: 'E' priority: 99 template: "" + + # Fetch SVG via ajax, inject into page using DOM link: (scope, elem, attr)-> if /.svg/.test attr.path # Only do this if we've got an svg $.ajax diff --git a/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee b/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee index ab2e0a6e3d..55c5a311da 100644 --- a/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee @@ -1,4 +1,5 @@ Darkswarm.directive 'scrollAfterLoad', ($timeout, $location, $document)-> + # Scroll to an element on page load restrict: "A" link: (scope, element, attr) -> if scope.$last is true diff --git a/app/assets/javascripts/darkswarm/directives/scrollto.js.coffee b/app/assets/javascripts/darkswarm/directives/scrollto.js.coffee index 3db3446596..a63337dc70 100644 --- a/app/assets/javascripts/darkswarm/directives/scrollto.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/scrollto.js.coffee @@ -1,4 +1,6 @@ Darkswarm.directive "ofnScrollTo", ($location, $anchorScroll)-> + # Onclick sets $location.hash to attrs.ofnScrollTo + # Then triggers anchorScroll restrict: 'A' link: (scope, element, attrs)-> element.bind 'click', (ev)-> diff --git a/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee index 3f752f8a3d..07656d84b7 100644 --- a/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee @@ -1,4 +1,5 @@ Darkswarm.directive "shippingTypeSelector", (FilterSelectorsService)-> + # Builds selector for shipping types restrict: 'E' replace: true templateUrl: 'shipping_type_selector.html' diff --git a/app/assets/javascripts/darkswarm/directives/shop_variant.js.coffee b/app/assets/javascripts/darkswarm/directives/shop_variant.js.coffee new file mode 100644 index 0000000000..e4cbed11c5 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/shop_variant.js.coffee @@ -0,0 +1,6 @@ +Darkswarm.directive "shopVariant", -> + restrict: 'E' + replace: true + templateUrl: 'shop_variant.html' + scope: + variant: '=' diff --git a/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee index a682176d77..633ecd22f4 100644 --- a/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee @@ -1,4 +1,6 @@ Darkswarm.directive "taxonSelector", (FilterSelectorsService)-> + # Automatically builds activeSelectors for taxons + # Lots of magic here restrict: 'E' replace: true scope: @@ -8,7 +10,7 @@ Darkswarm.directive "taxonSelector", (FilterSelectorsService)-> link: (scope, elem, attr)-> selectors_by_id = {} - selectors = ["foo"] + selectors = null # To get scoping/closure right scope.emit = -> scope.results = selectors.filter (selector)-> @@ -16,6 +18,7 @@ Darkswarm.directive "taxonSelector", (FilterSelectorsService)-> .map (selector)-> selector.taxon.id + # Build hash of unique taxons, each of which gets an ActiveSelector scope.selectors = -> taxons = {} selectors = [] @@ -25,7 +28,11 @@ Darkswarm.directive "taxonSelector", (FilterSelectorsService)-> if object.supplied_taxons for taxon in object.supplied_taxons taxons[taxon.id] = taxon - + + # Generate a selector for each taxon. + # 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 if selector = selectors_by_id[id] selectors.push selector diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index 2d91eab370..def1c9b6d0 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -19,7 +19,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> $http.post('/orders/populate', @data()).success (data, status)=> @saved() .error (response, status)=> - alert "There was an error on the server! Please refresh the page" + # TODO what shall we do here? data: => variants = {} @@ -43,6 +43,9 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> @line_items.filter (li)-> li.quantity > 0 + empty: => + @line_items_present().length == 0 + total: => @line_items_present().map (li)-> li.variant.getPrice() @@ -57,6 +60,6 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> create_line_item: (variant)-> variant.line_item = variant: variant - quantity: 0 + quantity: null max_quantity: null @line_items.push variant.line_item diff --git a/app/assets/javascripts/darkswarm/services/current_order.js.coffee b/app/assets/javascripts/darkswarm/services/current_order.js.coffee index a0714dc005..cd2402f0c0 100644 --- a/app/assets/javascripts/darkswarm/services/current_order.js.coffee +++ b/app/assets/javascripts/darkswarm/services/current_order.js.coffee @@ -1,5 +1,3 @@ Darkswarm.factory 'CurrentOrder', (currentOrder) -> new class CurrentOrder order: currentOrder - empty: => - @order.line_items.length == 0 diff --git a/app/assets/javascripts/darkswarm/services/products.js.coffee b/app/assets/javascripts/darkswarm/services/products.js.coffee index cc4ffbf259..5819c7b661 100644 --- a/app/assets/javascripts/darkswarm/services/products.js.coffee +++ b/app/assets/javascripts/darkswarm/services/products.js.coffee @@ -28,6 +28,8 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Car for product in @products if product.variants product.variants = (Variants.register variant for variant in product.variants) + variant.product = product for variant in product.variants + product.master.product = product product.master = Variants.register product.master if product.master registerVariantsWithCart: -> @@ -44,5 +46,5 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Car product.price = Math.min.apply(null, prices) product.hasVariants = product.variants?.length > 0 - product.primaryImage = product.images[0]?.small_url + product.primaryImage = product.images[0]?.small_url if product.images product.primaryImageOrMissing = product.primaryImage || "/assets/noimage/small.png" diff --git a/app/assets/javascripts/darkswarm/services/variants.js.coffee b/app/assets/javascripts/darkswarm/services/variants.js.coffee index d313458b75..0f231ac030 100644 --- a/app/assets/javascripts/darkswarm/services/variants.js.coffee +++ b/app/assets/javascripts/darkswarm/services/variants.js.coffee @@ -7,4 +7,5 @@ Darkswarm.factory 'Variants', -> extend: (variant)-> variant.getPrice = -> variant.price * variant.line_item.quantity + variant.basePricePercentage = Math.round(variant.base_price / variant.price * 100) variant diff --git a/app/assets/javascripts/templates/price_breakdown.html.haml b/app/assets/javascripts/templates/price_breakdown.html.haml index 5e7c82e0cf..9317356d02 100644 --- a/app/assets/javascripts/templates/price_breakdown.html.haml +++ b/app/assets/javascripts/templates/price_breakdown.html.haml @@ -1,4 +1,35 @@ -.joyride-tip-guide{"ng-class" => "{ in: tt_isOpen, fade: tt_animation }"} - %span.joyride-nub.bottom +.joyride-tip-guide{bindonce: true, "ng-class" => "{ in: tt_isOpen, fade: tt_animation }"} + %span.joyride-nub.right .joyride-content-wrapper - {{ variant.id }} + .collapsed{"ng-show" => "!expanded"} + %price-percentage{percentage: 'variant.basePricePercentage'} + %a{"ng-click" => "expanded = !expanded"} + Full price breakdown + %i.ofn-i_005-caret-down + + .expanded{"ng-show" => "expanded"} + %ul + %li.cost + .right {{ variant.base_price | currency }} + Cost + %li{"bo-if" => "variant.fees.admin"} + .right {{ variant.fees.admin | currency }} + Admin fee + %li{"bo-if" => "variant.fees.sales"} + .right {{ variant.fees.sales | currency }} + Sales fee + %li{"bo-if" => "variant.fees.packing"} + .right {{ variant.fees.packing | currency }} + Packing fee + %li{"bo-if" => "variant.fees.transport"} + .right {{ variant.fees.transport | currency }} + Transport fee + %li + %strong + .right = {{ variant.price | currency }} +   + + %a{"ng-click" => "expanded = !expanded"} + Price graph + %i.ofn-i_006-caret-up + diff --git a/app/assets/javascripts/templates/price_breakdown_button.html.haml b/app/assets/javascripts/templates/price_breakdown_button.html.haml new file mode 100644 index 0000000000..8a86ed2618 --- /dev/null +++ b/app/assets/javascripts/templates/price_breakdown_button.html.haml @@ -0,0 +1,2 @@ +%button.graph-button{"ng-class" => "{open: tt_isOpen}"} + %i.ofn-i-058-graph diff --git a/app/assets/javascripts/templates/price_percentage.html.haml b/app/assets/javascripts/templates/price_percentage.html.haml new file mode 100644 index 0000000000..4982f63eb7 --- /dev/null +++ b/app/assets/javascripts/templates/price_percentage.html.haml @@ -0,0 +1,5 @@ +.progress + .right Fees + .meter + Cost + diff --git a/app/assets/javascripts/templates/product_modal.html.haml b/app/assets/javascripts/templates/product_modal.html.haml index 8b35cf79c5..31f51c6655 100644 --- a/app/assets/javascripts/templates/product_modal.html.haml +++ b/app/assets/javascripts/templates/product_modal.html.haml @@ -3,7 +3,7 @@ %img.product-img{"ng-src" => "{{product.primaryImage}}", "ng-if" => "product.primaryImage"} .columns.small-12.large-6.product-header %h2 - %render-svg{path: "{{product.primary_taxon.icon}}"} + / %render-svg{path: "{{product.primary_taxon.icon}}"} {{product.name}} %p {{product.description}} %ng-include{src: "'partials/close.html'"} diff --git a/app/views/shop/products/_variants.html.haml b/app/assets/javascripts/templates/shop_variant.html.haml similarity index 72% rename from app/views/shop/products/_variants.html.haml rename to app/assets/javascripts/templates/shop_variant.html.haml index 3427d23a5f..a6db8b4e41 100644 --- a/app/views/shop/products/_variants.html.haml +++ b/app/assets/javascripts/templates/shop_variant.html.haml @@ -1,16 +1,15 @@ -.row.variants{bindonce: true, - "ng-repeat" => "variant in product.variants track by variant.id"} - +.variants.row .small-12.medium-4.large-4.columns.variant-name .table-cell .inline {{ variant.name_to_display }} - .bulk-buy.inline{"bo-if" => "product.group_buy"} + .bulk-buy.inline{"bo-if" => "variant.product.group_buy"} %i.ofn-i_056-bulk>< %em>< \ Bulk -# WITHOUT GROUP BUY - .small-5.medium-3.large-3.columns.text-right{"bo-if" => "!product.group_buy"} + .small-5.medium-3.large-3.columns.text-right{"bo-if" => "!variant.product.group_buy"} + %input{type: :number, value: nil, min: 0, @@ -22,7 +21,7 @@ -# WITH GROUP BUY - .small-5.medium-3.large-3.columns.text-right{"bo-if" => "product.group_buy"} + .small-5.medium-3.large-3.columns.text-right{"bo-if" => "variant.product.group_buy"} %span.bulk-input-container %span.bulk-input %input.bulk.first{type: :number, @@ -33,7 +32,7 @@ "ofn-disable-scroll" => true, max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} - %span.bulk-input{"bo-if" => "product.group_buy"} + %span.bulk-input{"bo-if" => "variant.product.group_buy"} %input.bulk.second{type: :number, min: 0, "ng-model" => "variant.line_item.max_quantity", @@ -51,8 +50,11 @@ %i.ofn-i_009-close {{ variant.price | currency }} - / %button.graph-button{popover: "This is the popover text", "popover-title" => "The title.", "popover-animation" => "true", "popover-trigger" =>"mouseenter", "popover-placement" => "top", "tabindex" => "-1"} - / %i.ofn-i-058-graph + -# Now in a template in app/assets/javascripts/templates ! + %price-breakdown{"price-breakdown" => "_", variant: "variant", + "price-breakdown-append-to-body" => "true", + "price-breakdown-placement" => "left", + "price-breakdown-animation" => true} .small-12.medium-2.large-2.columns.total-price.text-right .table-cell diff --git a/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass b/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass index fec2eae4a6..b79350818f 100644 --- a/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-popovers.css.sass @@ -1,37 +1,100 @@ @import mixins -.darkswarm - product - // Pop over +// .darkswarm +// product - // Foundation overrides - .joyride-tip-guide - // JS needs to be tweaked to adjust for left alignment - this is dynamic can't rewrite in CSS - background-color: #ebebeb - border: 1px solid #a5a5a5 - color: #1f1f1f - - h1, h2, h3, h4, h5, h6 - color: #1f1f1f - .joyride-nub.bottom - border-color: #a5a5a5 !important - border-bottom-color: transparent !important - border-left-color: transparent !important - border-right-color: transparent !important +// Pop over +// Foundation overrides +.joyride-tip-guide + // JS needs to be tweaked to adjust for left alignment - this is dynamic can't rewrite in CSS + background-color: #999 + color: #1f1f1f + @include box-shadow(0 1px 2px 0 rgba(0,0,0,0.7)) - button.graph-button - padding: 0 + .joyride-content-wrapper + padding: 1.125rem 1.25rem 1.5rem + padding: 1rem + margin: 1% + width: 98% + background-color: white + + h1, h2, h3, h4, h5, h6 + color: #1f1f1f + + .joyride-nub.right + top: 40px + border-color: #999 !important + border-top-color: transparent !important + border-right-color: transparent !important + border-bottom-color: transparent !important + + .progress + background-color: #13bf85 + padding: 0 + border: none + color: white + font-size: 0.75rem + font-style: oblique + line-height: 1 + height: auto + .right + padding: 0.5rem 0.25rem 0 0 + .meter + background-color: #0b8c61 + padding: 0.5rem 0.25rem + border-right: 1px solid #539f92 + + .expanded + ul, li + list-style: none margin: 0 - @include border-radius(0) - display: inline - background: none + font-size: 0.875rem + li + background-color: #13bf85 + padding: 0 0.25rem + margin-bottom: 2px + color: white + li.cost + background-color: #0b8c61 + li:last-child + margin-bottom: 0.75rem + + +button.graph-button + padding: 0 + margin: 0 + @include border-radius(99999) + display: inline + background-color: rgba(255,255,255,0.5) + padding: 0.2rem + + &:focus + background-color: rgba(255,255,255,0.5) + i.ofn-i-058-graph + color: #999 + + &:hover, &:active + background-color: rgba(255,255,255,1) + i.ofn-i-058-graph + color: $clr-brick-bright + + i.ofn-i-058-graph + color: #999 + margin: 0 + padding: 0 + font-size: 1rem + +button.graph-button.open + background-color: #999 + + &:hover, &:active, &:focus + background-color: #b2b2b2 + i.ofn-i-058-graph + color: $clr-brick-bright + + i.ofn-i-058-graph + color: $clr-brick + - i.ofn-i-058-graph - color: #999 - margin: 0 - padding: 0 - font-size: 1rem - &:hover, &:focus, &:active, &.active - color: #444 \ No newline at end of file diff --git a/app/assets/stylesheets/darkswarm/_shop-product-rows.css.sass b/app/assets/stylesheets/darkswarm/_shop-product-rows.css.sass index 2dbbf46a45..fbe5949e74 100644 --- a/app/assets/stylesheets/darkswarm/_shop-product-rows.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-product-rows.css.sass @@ -100,8 +100,10 @@ h3 font-size: 1.5rem margin: 0 - a h3 - color: black + h3 a + color: #222 + &:hover, &:focus, &:active + color: black diff --git a/app/assets/stylesheets/darkswarm/_shop-product-thumb.css.sass b/app/assets/stylesheets/darkswarm/_shop-product-thumb.css.sass index 8bc1550d54..7a69707a60 100644 --- a/app/assets/stylesheets/darkswarm/_shop-product-thumb.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-product-thumb.css.sass @@ -13,7 +13,7 @@ float: left display: block z-index: 999999 - background-color: #999 + background-color: white overflow: hidden @media all and (max-width: 768px) diff --git a/app/assets/stylesheets/darkswarm/images.css.sass b/app/assets/stylesheets/darkswarm/images.css.sass index 39a86a93d1..ce205f0dae 100644 --- a/app/assets/stylesheets/darkswarm/images.css.sass +++ b/app/assets/stylesheets/darkswarm/images.css.sass @@ -11,9 +11,9 @@ @include box-shadow(0 1px 2px 1px rgba(0,0,0,0.25)) .hero-img - background-color: #333 + border-bottom: 1px solid $disabled-bright width: 100% - min-height: 160px + min-height: 56px height: inherit max-height: 260px overflow: hidden diff --git a/app/assets/stylesheets/darkswarm/map.css.sass b/app/assets/stylesheets/darkswarm/map.css.sass index 2050cf5063..78d350d6be 100644 --- a/app/assets/stylesheets/darkswarm/map.css.sass +++ b/app/assets/stylesheets/darkswarm/map.css.sass @@ -1,6 +1,8 @@ // Place all the styles related to the map controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ +@import big-input + .map-container width: 100% map, .angular-google-map-container, google-map, .angular-google-map @@ -11,6 +13,14 @@ max-width: none height: auto - #pac-input - padding: 4px - font-size: 2em + #pac-input + @include big-input(#888, #333, $clr-brick) + @include big-input-static + font-size: 1.5rem + background: rgba(255,255,255,0.85) + width: 50% + margin-top: 1.2rem + @media all and (max-width: 768px) + width: 80% + &:active, &:focus, &.active + background: rgba(255,255,255, 1) diff --git a/app/assets/stylesheets/darkswarm/modals.css.sass b/app/assets/stylesheets/darkswarm/modals.css.sass index 182ab5bec0..086ab8b076 100644 --- a/app/assets/stylesheets/darkswarm/modals.css.sass +++ b/app/assets/stylesheets/darkswarm/modals.css.sass @@ -5,45 +5,56 @@ dialog, .reveal-modal border: none outline: none padding: 1rem - div - overflow: scroll - @media only screen and (min-width: 40.063em) - max-height: 580px - @media all and (max-width: 768px) - max-height: 440px - @media all and (max-width: 640px) - max-height: 400px - @media all and (max-width: 640px) - max-height: inherit - overflow: scroll + // TO DO: look at bigger issue scrolling on mobile device + overflow-y: scroll + @media only screen and (min-width: 40.063em) + max-height: 580px + @media all and (max-width: 768px) + max-height: 440px + @media all and (max-width: 640px) + max-height: 400px + @media all and (max-width: 640px) + max-height: inherit + overflow-y: scroll .reveal-modal-bg background-color: rgba(0,0,0,0.65) -dialog .close-reveal-modal.outside, .reveal-modal .close-reveal-modal.outside - top: -2.5rem - right: -2.5rem - font-size: 2rem - color: white +dialog .close-reveal-modal, .reveal-modal .close-reveal-modal + top: 0.45rem + right: 0.4rem + background-color: rgba(235,235,235,0.85) text-shadow: none - padding: 0.25rem + padding: 0.3rem @include border-radius(999999) - border: 1px solid transparent &:hover, &:active, &:focus - text-shadow: 0 1px 3px #333 - border: 1px solid white + background-color: rgba(235,235,235,1) + color: #333 - @media all and (max-width: 640px) - top: 0.5rem - right: 0.5rem - font-size: 2rem - color: white - text-shadow: none - padding: 0.25rem - background-color: rgba(150,150,150,0.85) - @include border-radius(999999) - border: 1px solid transparent - &:hover, &:active, &:focus - text-shadow: 0 1px 3px #333 - border: 1px solid white +// dialog .close-reveal-modal.outside, .reveal-modal .close-reveal-modal.outside +// top: -2.5rem +// right: -2.5rem +// font-size: 2rem +// color: white +// text-shadow: none +// padding: 0.25rem +// @include border-radius(999999) +// border: 1px solid transparent +// &:hover, &:active, &:focus +// text-shadow: 0 1px 3px #333 +// border: 1px solid white + +// @media all and (max-width: 640px) +// top: 0.5rem +// right: 0.5rem +// font-size: 2rem +// color: white +// text-shadow: none +// padding: 0.25rem +// background-color: rgba(150,150,150,0.85) +// @include border-radius(999999) +// border: 1px solid transparent +// &:hover, &:active, &:focus +// text-shadow: 0 1px 3px #333 +// border: 1px solid white diff --git a/app/assets/stylesheets/darkswarm/shopping-cart.css.sass b/app/assets/stylesheets/darkswarm/shopping-cart.css.sass index 745dceb70f..3a63bb7a2e 100644 --- a/app/assets/stylesheets/darkswarm/shopping-cart.css.sass +++ b/app/assets/stylesheets/darkswarm/shopping-cart.css.sass @@ -13,13 +13,20 @@ right: 10px top: 55px width: 400px + @media screen and (max-width: 640px) + width: 96% .joyride-nub right: 22px !important left: auto + ul, li + list-style: none + margin-left: 0 + li float: none + .row .columns padding-left: 0.25rem padding-right: 0.25rem diff --git a/app/assets/stylesheets/darkswarm/ui.css.sass b/app/assets/stylesheets/darkswarm/ui.css.sass index bd740ec614..9d28e16787 100644 --- a/app/assets/stylesheets/darkswarm/ui.css.sass +++ b/app/assets/stylesheets/darkswarm/ui.css.sass @@ -46,6 +46,7 @@ .button, button @include border-radius(0.5em) + outline: none // Turn off blue highlight on chrome .button.primary, button.primary font-family: 'Open Sans', Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index f2ad8fee3e..d5a1371876 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -1,5 +1,3 @@ -require 'open_food_network/enterprise_fee_applicator' - class OrderCycle < ActiveRecord::Base belongs_to :coordinator, :class_name => 'Enterprise' has_and_belongs_to_many :coordinator_fees, :class_name => 'EnterpriseFee', :join_table => 'coordinator_fees' @@ -165,77 +163,17 @@ class OrderCycle < ActiveRecord::Base exchange_for_distributor(distributor).andand.pickup_instructions end - - # -- Fees - - # TODO: The boundary of this class is ill-defined here. OrderCycle should not know about - # EnterpriseFeeApplicator. Clients should be able to query it for relevant EnterpriseFees. - # This logic would fit better in another service object. - - def fees_for(variant, distributor) - per_item_enterprise_fee_applicators_for(variant, distributor).sum do |applicator| - # Spree's Calculator interface accepts Orders or LineItems, - # so we meet that interface with a struct. - # Amount is faked, this is a method on LineItem - line_item = OpenStruct.new variant: variant, quantity: 1, amount: variant.price - applicator.enterprise_fee.compute_amount(line_item) - end + def exchanges_carrying(variant, distributor) + exchanges.supplying_to(distributor).with_variant(variant) end - def create_line_item_adjustments_for(line_item) - variant = line_item.variant - distributor = line_item.order.distributor - - per_item_enterprise_fee_applicators_for(variant, distributor).each do |applicator| - applicator.create_line_item_adjustment(line_item) - end - end - - def create_order_adjustments_for(order) - per_order_enterprise_fee_applicators_for(order).each do |applicator| - applicator.create_order_adjustment(order) - end + def exchanges_supplying(order) + exchanges.supplying_to(order.distributor).with_any_variant(order.variants) end private - # -- Fees - def per_item_enterprise_fee_applicators_for(variant, distributor) - fees = [] - - exchanges_carrying(variant, distributor).each do |exchange| - exchange.enterprise_fees.per_item.each do |enterprise_fee| - fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, variant, exchange.role) - end - end - - coordinator_fees.per_item.each do |enterprise_fee| - fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, variant, 'coordinator') - end - - fees - end - - def per_order_enterprise_fee_applicators_for(order) - fees = [] - - exchanges_supplying(order).each do |exchange| - exchange.enterprise_fees.per_order.each do |enterprise_fee| - fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, nil, exchange.role) - end - end - - coordinator_fees.per_order.each do |enterprise_fee| - fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, nil, 'coordinator') - end - - fees - end - - - # -- Misc - # If a product without variants is added to an order cycle, and then some variants are added # to that product, then the master variant is still part of the order cycle, but customers # should not be able to purchase it. @@ -246,12 +184,4 @@ class OrderCycle < ActiveRecord::Base distributed_variants.include?(product.master) && (product.variants & distributed_variants).empty? end - - def exchanges_carrying(variant, distributor) - exchanges.supplying_to(distributor).with_variant(variant) - end - - def exchanges_supplying(order) - exchanges.supplying_to(order.distributor).with_any_variant(order.variants) - end end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 4f2be99fa8..fcc6054f3b 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -1,5 +1,6 @@ -require 'open_food_network/feature_toggle' +require 'open_food_network/enterprise_fee_calculator' require 'open_food_network/distribution_change_validator' +require 'open_food_network/feature_toggle' ActiveSupport::Notifications.subscribe('spree.order.contents_changed') do |name, start, finish, id, payload| payload[:order].reload.update_distribution_charge! @@ -133,7 +134,7 @@ Spree::Order.class_eval do line_items.each do |line_item| if provided_by_order_cycle? line_item - order_cycle.create_line_item_adjustments_for line_item + OpenFoodNetwork::EnterpriseFeeCalculator.new.create_line_item_adjustments_for line_item else pd = product_distribution_for line_item @@ -141,7 +142,9 @@ Spree::Order.class_eval do end end - order_cycle.create_order_adjustments_for self if order_cycle + if order_cycle + OpenFoodNetwork::EnterpriseFeeCalculator.new.create_order_adjustments_for self + end end def set_variant_attributes(variant, attributes) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 63ae5e0f3a..7d4f178488 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -1,3 +1,4 @@ +require 'open_food_network/enterprise_fee_calculator' require 'open_food_network/option_value_namer' Spree::Variant.class_eval do @@ -44,7 +45,11 @@ Spree::Variant.class_eval do end def fees_for(distributor, order_cycle) - order_cycle.fees_for(self, distributor) + OpenFoodNetwork::EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for self + end + + def fees_by_type_for(distributor, order_cycle) + OpenFoodNetwork::EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for self end diff --git a/app/serializers/api/variant_serializer.rb b/app/serializers/api/variant_serializer.rb index c5dbe05634..e03f1e0ec8 100644 --- a/app/serializers/api/variant_serializer.rb +++ b/app/serializers/api/variant_serializer.rb @@ -1,8 +1,16 @@ class Api::VariantSerializer < ActiveModel::Serializer attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display, - :on_demand, :price + :on_demand, :price, :fees, :base_price def price object.price_with_fees(options[:current_distributor], options[:current_order_cycle]) end + + def base_price + object.price + end + + def fees + object.fees_by_type_for(options[:current_distributor], options[:current_order_cycle]) + end end diff --git a/app/views/shared/menu/_cart.html.haml b/app/views/shared/menu/_cart.html.haml index 2d8fbebcb9..907fda0eff 100644 --- a/app/views/shared/menu/_cart.html.haml +++ b/app/views/shared/menu/_cart.html.haml @@ -1,4 +1,4 @@ -%span.cart-span{"ng-controller" => "CartCtrl"} +%span.cart-span{"ng-controller" => "CartCtrl", "ng-class" => "{ dirty: Cart.dirty }"} %a#cart.icon{cart: true} %span.nav-branded %i.ofn-i_027-shopping-cart diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index 25004f97b4..a10edd7fb9 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -20,12 +20,8 @@ "ng-repeat" => "product in filteredProducts = (Products.products | products:query | taxons:activeTaxons | orderBy:ordering.order) track by product.id "} = render partial: "shop/products/summary" - - %span{"bo-if" => "product.hasVariants"} - = render partial: "shop/products/variants" - - .variants.row{"bo-if" => "!product.hasVariants"} - = render partial: "shop/products/master" + %shop-variant{variant: 'product.master', "bo-if" => "!product.hasVariants"} + %shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants track by variant.id"} %product{"ng-show" => "Products.loading"} .row.summary diff --git a/app/views/shop/products/_master.html.haml b/app/views/shop/products/_master.html.haml deleted file mode 100644 index f12df1b881..0000000000 --- a/app/views/shop/products/_master.html.haml +++ /dev/null @@ -1,58 +0,0 @@ -.small-12.medium-4.large-4.columns.variant-name - .table-cell - .inline {{ product.master.name_to_display }} - .bulk-buy.inline{"bo-if" => "product.group_buy"} - %i.ofn-i_056-bulk>< - %em>< - \ Bulk - --# WITHOUT GROUP BUY -.small-5.medium-3.large-3.columns.text-right{"bo-if" => "!product.group_buy"} - %input{type: :number, - min: 0, - placeholder: "0", - "ofn-disable-scroll" => true, - max: "{{product.on_demand && 9999 || product.count_on_hand }}", - name: "variants[{{product.master.id}}]", - "ng-model" => "product.master.line_item.quantity", - id: "variants_{{product.master.id}}"} - --# WITH GROUP BUY -.small-5.medium-3.large-3.columns.text-right{"bo-if" => "product.group_buy"} - %span.bulk-input-container - %span.bulk-input - %input.bulk.first{type: :number, - min: 0, - "ng-model" => "product.master.line_item.quantity", - placeholder: "min", - "ofn-disable-scroll" => true, - max: "{{product.on_demand && 9999 || product.count_on_hand }}", - name: "variants[{{product.master.id}}]", - id: "variants_{{product.master.id}}"} - - %span.bulk-input{"bo-if" => "product.group_buy"} - %input.bulk.second{type: :number, - min: 0, - "ng-model" => "product.master.line_item.max_quantity", - placeholder: "max", - "ofn-disable-scroll" => true, - max: "{{product.on_demand && 9999 || product.count_on_hand }}", - name: "variant_attributes[{{product.master.id}}][max_quantity]"} - -.small-3.medium-1.large-1.columns.variant-unit - .table-cell - %em {{ product.master.unit_to_display }} - -.small-4.medium-2.large-2.columns.variant-price - .table-cell - %i.ofn-i_009-close - {{ product.master.price | currency }} - -#%button.graph-button{"price-breakdown" => "_", - -#"variant" => "product.master", - -#"price-breakdown-animation" => "true"} - -#%i.ofn-i-058-graph - -.small-12.medium-2.large-2.columns.total-price.text-right - .table-cell - %strong - {{ product.master.getPrice() | currency }} diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml index 77f43702cc..25c71f276a 100644 --- a/app/views/shop/products/_summary.html.haml +++ b/app/views/shop/products/_summary.html.haml @@ -4,8 +4,9 @@ .row.summary .small-9.medium-10.large-11.columns.summary-header - %a{"ng-click" => "triggerProductModal()"} - %h3 {{ product.name }} + %h3 + %a{"ng-click" => "triggerProductModal()"} + {{ product.name }} %em from %span diff --git a/config/ng-test.conf.js b/config/ng-test.conf.js index 33f95483cf..eadaf984ae 100644 --- a/config/ng-test.conf.js +++ b/config/ng-test.conf.js @@ -8,7 +8,6 @@ module.exports = function(config) { APPLICATION_SPEC, 'app/assets/javascripts/shared/jquery-1.8.0.js', // TODO: Can we link to Rails' jquery? 'app/assets/javascripts/shared/jquery.timeago.js', - 'app/assets/javascripts/shared/mm-foundation-tpls-0.2.0-SNAPSHOT.js', 'app/assets/javascripts/shared/angular-local-storage.js', 'app/assets/javascripts/shared/bindonce.min.js', 'app/assets/javascripts/shared/ng-infinite-scroll.min.js', diff --git a/lib/open_food_network/enterprise_fee_calculator.rb b/lib/open_food_network/enterprise_fee_calculator.rb new file mode 100644 index 0000000000..c8070f2ab2 --- /dev/null +++ b/lib/open_food_network/enterprise_fee_calculator.rb @@ -0,0 +1,88 @@ +require 'open_food_network/enterprise_fee_applicator' + +module OpenFoodNetwork + class EnterpriseFeeCalculator + def initialize(distributor=nil, order_cycle=nil) + @distributor = distributor + @order_cycle = order_cycle + end + + + def fees_for(variant) + per_item_enterprise_fee_applicators_for(variant).sum do |applicator| + calculate_fee_for variant, applicator + end + end + + def fees_by_type_for(variant) + per_item_enterprise_fee_applicators_for(variant).inject({}) do |fees, applicator| + fees[applicator.enterprise_fee.fee_type.to_sym] ||= 0 + fees[applicator.enterprise_fee.fee_type.to_sym] += calculate_fee_for variant, applicator + fees + end.select { |fee_type, amount| amount > 0 } + end + + + def create_line_item_adjustments_for(line_item) + variant = line_item.variant + @distributor = line_item.order.distributor + @order_cycle = line_item.order.order_cycle + + per_item_enterprise_fee_applicators_for(variant).each do |applicator| + applicator.create_line_item_adjustment(line_item) + end + end + + def create_order_adjustments_for(order) + @distributor = order.distributor + @order_cycle = order.order_cycle + + per_order_enterprise_fee_applicators_for(order).each do |applicator| + applicator.create_order_adjustment(order) + end + end + + + private + + def calculate_fee_for(variant, applicator) + # Spree's Calculator interface accepts Orders or LineItems, + # so we meet that interface with a struct. + # Amount is faked, this is a method on LineItem + line_item = OpenStruct.new variant: variant, quantity: 1, amount: variant.price + applicator.enterprise_fee.compute_amount(line_item) + end + + def per_item_enterprise_fee_applicators_for(variant) + fees = [] + + @order_cycle.exchanges_carrying(variant, @distributor).each do |exchange| + exchange.enterprise_fees.per_item.each do |enterprise_fee| + fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, variant, exchange.role) + end + end + + @order_cycle.coordinator_fees.per_item.each do |enterprise_fee| + fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, variant, 'coordinator') + end + + fees + end + + def per_order_enterprise_fee_applicators_for(order) + fees = [] + + @order_cycle.exchanges_supplying(order).each do |exchange| + exchange.enterprise_fees.per_order.each do |enterprise_fee| + fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, nil, exchange.role) + end + end + + @order_cycle.coordinator_fees.per_order.each do |enterprise_fee| + fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, nil, 'coordinator') + end + + fees + end + end +end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 288e628b38..56c578f91b 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -139,7 +139,9 @@ feature "As a consumer I want to shop with a distributor", js: true do it "should save group buy data to ze cart" do fill_in "variants[#{product.master.id}]", with: 5 fill_in "variant_attributes[#{product.master.id}][max_quantity]", with: 9 - sleep 5 + + wait_until { !cart_dirty } + li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last li.max_quantity.should == 9 li.quantity.should == 5 diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index 44654e7b32..d9239c79ad 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -8,6 +8,7 @@ //= require angular-backstretch.js //= require lodash.underscore.js //= require angular-flash.min.js +//= require shared/mm-foundation-tpls-0.2.2.min.js //= require moment angular.module('templates', []) 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 1d56a79d27..73ecd611ef 100644 --- a/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/controllers/products_controller_spec.js.coffee @@ -3,6 +3,7 @@ describe 'ProductsCtrl', -> scope = null event = null Products = null + Cart = {} beforeEach -> module('Darkswarm') @@ -15,7 +16,7 @@ describe 'ProductsCtrl', -> inject ($controller) -> scope = {} - ctrl = $controller 'ProductsCtrl', {$scope: scope, Products: Products, OrderCycle: OrderCycle} + ctrl = $controller 'ProductsCtrl', {$scope: scope, Products: Products, OrderCycle: OrderCycle, Cart: Cart} it 'fetches products from Products', -> expect(scope.Products.products).toEqual ['testy mctest'] diff --git a/spec/javascripts/unit/darkswarm/filters/filter_groups_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/filter_groups_spec.js.coffee index e7e2614f7f..0e85fe27d3 100644 --- a/spec/javascripts/unit/darkswarm/filters/filter_groups_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/filters/filter_groups_spec.js.coffee @@ -2,7 +2,7 @@ describe "filtering Groups", -> filterGroups = null groups = [{ name: "test" - long_description: "roger" + description: "roger" enterprises: [{ name: "kittens" }, { @@ -10,7 +10,7 @@ describe "filtering Groups", -> }] }, { name: "blankness" - long_description: "in the sky" + description: "in the sky" enterprises: [{ name: "ponies" }, { diff --git a/spec/javascripts/unit/darkswarm/filters/strip_url_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/strip_url_spec.js.coffee index f34df5cc71..ae0d6a7c8a 100644 --- a/spec/javascripts/unit/darkswarm/filters/strip_url_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/filters/strip_url_spec.js.coffee @@ -6,11 +6,8 @@ describe 'filtering urls', -> inject ($filter) -> filter = $filter('stripUrl') - it "removes http and www", -> - expect(filter("http://www.footle.com")).toEqual "footle.com" + it "removes http", -> + expect(filter("http://footle.com")).toEqual "footle.com" - it "removes https and www", -> - expect(filter("https://www.footle.com")).toEqual "footle.com" - - it "removes just www", -> - expect(filter("www.footle.com")).toEqual "footle.com" + it "removes https", -> + expect(filter("https://www.footle.com")).toEqual "www.footle.com" diff --git a/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee index af384adcb6..237cbcd369 100644 --- a/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee @@ -55,6 +55,12 @@ describe 'Products service', -> $httpBackend.flush() expect(Cart.line_items[0].variant).toBe Products.products[0].variants[0] + it "sets primaryImageOrMissing when no images are provided", -> + $httpBackend.expectGET("/shop/products").respond([product]) + $httpBackend.flush() + expect(Products.products[0].primaryImage).toBeUndefined() + expect(Products.products[0].primaryImageOrMissing).toEqual "/assets/noimage/small.png" + describe "determining the price to display for a product", -> it "displays the product price when the product does not have variants", -> $httpBackend.expectGET("/shop/products").respond([product]) @@ -66,4 +72,3 @@ describe 'Products service', -> $httpBackend.expectGET("/shop/products").respond([product]) $httpBackend.flush() expect(Products.products[0].price).toEqual 22 - diff --git a/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee index 278e0a6850..ac9865a142 100644 --- a/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee @@ -5,6 +5,8 @@ describe 'Variants service', -> beforeEach -> variant = id: 1 + base_price: 80.5 + price: 100 module 'Darkswarm' inject ($injector)-> Variants = $injector.get("Variants") @@ -19,3 +21,5 @@ describe 'Variants service', -> it "will return the same object as passed", -> expect(Variants.register(variant)).toBe variant + it "initialises base price percentage", -> + expect(Variants.register(variant).basePricePercentage).toEqual 81 diff --git a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb index d10a5e9b72..9ad1d222b3 100644 --- a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb +++ b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb @@ -27,7 +27,6 @@ module OpenFoodNetwork it "creates an adjustment for an order" do order = create(:order) - #line_item = create(:line_item) enterprise_fee = create(:enterprise_fee) product = create(:simple_product) diff --git a/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb b/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb new file mode 100644 index 0000000000..0ca7da4999 --- /dev/null +++ b/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb @@ -0,0 +1,159 @@ +require 'open_food_network/enterprise_fee_calculator' + +module OpenFoodNetwork + describe EnterpriseFeeCalculator do + describe "integration" do + let(:coordinator) { create(:distributor_enterprise) } + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle) } + let(:product) { create(:simple_product, price: 10.00) } + + describe "calculating fees for a variant" do + it "sums all the per-item fees for the variant in the specified hub + order cycle" do + enterprise_fee1 = create(:enterprise_fee, amount: 20) + enterprise_fee2 = create(:enterprise_fee, amount: 3) + enterprise_fee3 = create(:enterprise_fee, + calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) + + create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, + enterprise_fees: [enterprise_fee1, enterprise_fee2, enterprise_fee3], variants: [product.master]) + + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product.master).should == 23 + end + + it "sums percentage fees for the variant" do + enterprise_fee1 = create(:enterprise_fee, amount: 20, fee_type: "admin", calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 20)) + + create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, + enterprise_fees: [enterprise_fee1], variants: [product.master]) + + product.master.price.should == 10.00 + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product.master).should == 2.00 + end + end + + describe "calculating fees by type" do + let!(:ef_admin) { create(:enterprise_fee, fee_type: 'admin', amount: 1.23) } + let!(:ef_sales) { create(:enterprise_fee, fee_type: 'sales', amount: 4.56) } + let!(:ef_packing) { create(:enterprise_fee, fee_type: 'packing', amount: 7.89) } + let!(:ef_transport) { create(:enterprise_fee, fee_type: 'transport', amount: 0.12) } + let!(:exchange) { create(:exchange, order_cycle: order_cycle, + sender: coordinator, receiver: distributor, incoming: false, + enterprise_fees: [ef_admin, ef_sales, ef_packing, ef_transport], + variants: [product.master]) } + + it "returns a breakdown of fees" do + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for(product.master).should == {admin: 1.23, sales: 4.56, packing: 7.89, transport: 0.12} + end + + it "filters out zero fees" do + ef_admin.calculator.update_attribute :preferred_amount, 0 + + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for(product.master).should == {sales: 4.56, packing: 7.89, transport: 0.12} + end + end + + describe "creating adjustments" do + let(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) } + let!(:line_item) { create(:line_item, order: order, variant: product.master) } + let(:enterprise_fee_line_item) { create(:enterprise_fee) } + let(:enterprise_fee_order) { create(:enterprise_fee, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) } + let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, variants: [product.master]) } + + before { order.reload } + + it "creates adjustments for a line item" do + exchange.enterprise_fees << enterprise_fee_line_item + + EnterpriseFeeCalculator.new.create_line_item_adjustments_for line_item + + a = Spree::Adjustment.last + a.metadata.fee_name.should == enterprise_fee_line_item.name + end + + it "creates adjustments for an order" do + exchange.enterprise_fees << enterprise_fee_order + + EnterpriseFeeCalculator.new.create_order_adjustments_for order + + a = Spree::Adjustment.last + a.metadata.fee_name.should == enterprise_fee_order.name + end + end + end + + describe "creating adjustments for a line item" do + let(:oc) { OrderCycle.new } + let(:variant) { double(:variant) } + let(:distributor) { double(:distributor) } + let(:order) { double(:order, distributor: distributor, order_cycle: oc) } + let(:line_item) { double(:line_item, variant: variant, order: order) } + + it "creates an adjustment for each fee" do + applicator = double(:enterprise_fee_applicator) + applicator.should_receive(:create_line_item_adjustment).with(line_item) + + efc = EnterpriseFeeCalculator.new + efc.should_receive(:per_item_enterprise_fee_applicators_for).with(variant) { [applicator] } + + efc.create_line_item_adjustments_for line_item + end + + it "makes fee applicators for a line item" do + distributor = double(:distributor) + ef1 = double(:enterprise_fee) + ef2 = double(:enterprise_fee) + ef3 = double(:enterprise_fee) + incoming_exchange = double(:exchange, role: 'supplier') + outgoing_exchange = double(:exchange, role: 'distributor') + incoming_exchange.stub_chain(:enterprise_fees, :per_item) { [ef1] } + outgoing_exchange.stub_chain(:enterprise_fees, :per_item) { [ef2] } + + oc.stub(:exchanges_carrying) { [incoming_exchange, outgoing_exchange] } + oc.stub_chain(:coordinator_fees, :per_item) { [ef3] } + + efc = EnterpriseFeeCalculator.new(distributor, oc) + efc.send(:per_item_enterprise_fee_applicators_for, line_item.variant).should == + [OpenFoodNetwork::EnterpriseFeeApplicator.new(ef1, line_item.variant, 'supplier'), + OpenFoodNetwork::EnterpriseFeeApplicator.new(ef2, line_item.variant, 'distributor'), + OpenFoodNetwork::EnterpriseFeeApplicator.new(ef3, line_item.variant, 'coordinator')] + end + end + + describe "creating adjustments for an order" do + let(:oc) { OrderCycle.new } + let(:distributor) { double(:distributor) } + let(:order) { double(:order, distributor: distributor, order_cycle: oc) } + + it "creates an adjustment for each fee" do + applicator = double(:enterprise_fee_applicator) + applicator.should_receive(:create_order_adjustment).with(order) + + efc = EnterpriseFeeCalculator.new + efc.should_receive(:per_order_enterprise_fee_applicators_for).with(order) { [applicator] } + + efc.create_order_adjustments_for order + end + + it "makes fee applicators for an order" do + distributor = double(:distributor) + ef1 = double(:enterprise_fee) + ef2 = double(:enterprise_fee) + ef3 = double(:enterprise_fee) + incoming_exchange = double(:exchange, role: 'supplier') + outgoing_exchange = double(:exchange, role: 'distributor') + incoming_exchange.stub_chain(:enterprise_fees, :per_order) { [ef1] } + outgoing_exchange.stub_chain(:enterprise_fees, :per_order) { [ef2] } + + oc.stub(:exchanges_supplying) { [incoming_exchange, outgoing_exchange] } + oc.stub_chain(:coordinator_fees, :per_order) { [ef3] } + + efc = EnterpriseFeeCalculator.new(distributor, oc) + efc.send(:per_order_enterprise_fee_applicators_for, order).should == + [OpenFoodNetwork::EnterpriseFeeApplicator.new(ef1, nil, 'supplier'), + OpenFoodNetwork::EnterpriseFeeApplicator.new(ef2, nil, 'distributor'), + OpenFoodNetwork::EnterpriseFeeApplicator.new(ef3, nil, 'coordinator')] + end + end + end +end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index e4f979dc5b..9785d5ed2d 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -368,107 +368,6 @@ describe OrderCycle do end end - describe "calculating fees for a variant via a particular distributor" do - it "sums all the per-item fees for the variant in the specified hub + order cycle" do - coordinator = create(:distributor_enterprise) - distributor = create(:distributor_enterprise) - order_cycle = create(:simple_order_cycle) - enterprise_fee1 = create(:enterprise_fee, amount: 20) - enterprise_fee2 = create(:enterprise_fee, amount: 3) - enterprise_fee3 = create(:enterprise_fee, - calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) - product = create(:simple_product) - - create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, - enterprise_fees: [enterprise_fee1, enterprise_fee2, enterprise_fee3], variants: [product.master]) - - order_cycle.fees_for(product.master, distributor).should == 23 - end - - - it "sums percentage fees for the variant" do - coordinator = create(:distributor_enterprise) - distributor = create(:distributor_enterprise) - order_cycle = create(:simple_order_cycle) - enterprise_fee1 = create(:enterprise_fee, amount: 20, fee_type: "admin", calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 20)) - product = create(:simple_product, price: 10.00) - - create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, - enterprise_fees: [enterprise_fee1], variants: [product.master]) - - product.master.price.should == 10.00 - order_cycle.fees_for(product.master, distributor).should == 2.00 - end - end - - describe "creating adjustments for a line item" do - let(:oc) { OrderCycle.new } - let(:variant) { double(:variant) } - let(:distributor) { double(:distributor) } - let(:order) { double(:order, distributor: distributor) } - let(:line_item) { double(:line_item, variant: variant, order: order) } - - it "creates an adjustment for each fee" do - applicator = double(:enterprise_fee_applicator) - applicator.should_receive(:create_line_item_adjustment).with(line_item) - oc.should_receive(:per_item_enterprise_fee_applicators_for).with(variant, distributor) { [applicator] } - - oc.send(:create_line_item_adjustments_for, line_item) - end - - it "makes fee applicators for a line item" do - distributor = double(:distributor) - ef1 = double(:enterprise_fee) - ef2 = double(:enterprise_fee) - ef3 = double(:enterprise_fee) - incoming_exchange = double(:exchange, role: 'supplier') - outgoing_exchange = double(:exchange, role: 'distributor') - incoming_exchange.stub_chain(:enterprise_fees, :per_item) { [ef1] } - outgoing_exchange.stub_chain(:enterprise_fees, :per_item) { [ef2] } - - oc.stub(:exchanges_carrying) { [incoming_exchange, outgoing_exchange] } - oc.stub_chain(:coordinator_fees, :per_item) { [ef3] } - - oc.send(:per_item_enterprise_fee_applicators_for, line_item.variant, distributor).should == - [OpenFoodNetwork::EnterpriseFeeApplicator.new(ef1, line_item.variant, 'supplier'), - OpenFoodNetwork::EnterpriseFeeApplicator.new(ef2, line_item.variant, 'distributor'), - OpenFoodNetwork::EnterpriseFeeApplicator.new(ef3, line_item.variant, 'coordinator')] - end - end - - describe "creating adjustments for an order" do - let(:oc) { OrderCycle.new } - let(:distributor) { double(:distributor) } - let(:order) { double(:order, distributor: distributor) } - - it "creates an adjustment for each fee" do - applicator = double(:enterprise_fee_applicator) - applicator.should_receive(:create_order_adjustment).with(order) - oc.should_receive(:per_order_enterprise_fee_applicators_for).with(order) { [applicator] } - - oc.send(:create_order_adjustments_for, order) - end - - it "makes fee applicators for an order" do - distributor = double(:distributor) - ef1 = double(:enterprise_fee) - ef2 = double(:enterprise_fee) - ef3 = double(:enterprise_fee) - incoming_exchange = double(:exchange, role: 'supplier') - outgoing_exchange = double(:exchange, role: 'distributor') - incoming_exchange.stub_chain(:enterprise_fees, :per_order) { [ef1] } - outgoing_exchange.stub_chain(:enterprise_fees, :per_order) { [ef2] } - - oc.stub(:exchanges_supplying) { [incoming_exchange, outgoing_exchange] } - oc.stub_chain(:coordinator_fees, :per_order) { [ef3] } - - oc.send(:per_order_enterprise_fee_applicators_for, order).should == - [OpenFoodNetwork::EnterpriseFeeApplicator.new(ef1, nil, 'supplier'), - OpenFoodNetwork::EnterpriseFeeApplicator.new(ef2, nil, 'distributor'), - OpenFoodNetwork::EnterpriseFeeApplicator.new(ef3, nil, 'coordinator')] - end - end - describe "finding recently closed order cycles" do it "should give the most recently closed order cycle for a distributor" do distributor = create(:distributor_enterprise) diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 898a08d075..085ee615c6 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -82,8 +82,10 @@ describe Spree::Order do subject.stub(:provided_by_order_cycle?) { true } order_cycle = double(:order_cycle) - order_cycle.should_receive(:create_line_item_adjustments_for).with(line_item) - order_cycle.stub(:create_order_adjustments_for) + OpenFoodNetwork::EnterpriseFeeCalculator.any_instance. + should_receive(:create_line_item_adjustments_for). + with(line_item) + OpenFoodNetwork::EnterpriseFeeCalculator.any_instance.stub(:create_order_adjustments_for) subject.stub(:order_cycle) { order_cycle } subject.update_distribution_charge! @@ -94,7 +96,10 @@ describe Spree::Order do subject.stub(:line_items) { [] } order_cycle = double(:order_cycle) - order_cycle.should_receive(:create_order_adjustments_for).with(subject) + OpenFoodNetwork::EnterpriseFeeCalculator.any_instance. + should_receive(:create_order_adjustments_for). + with(subject) + subject.stub(:order_cycle) { order_cycle } subject.update_distribution_charge! diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 5ddff963df..7fb160ce3f 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -82,17 +82,32 @@ module Spree describe "calculating the fees" do - it "delegates to order cycle" do + it "delegates to EnterpriseFeeCalculator" do distributor = double(:distributor) order_cycle = double(:order_cycle) variant = Variant.new - order_cycle.should_receive(:fees_for).with(variant, distributor) { 23 } + OpenFoodNetwork::EnterpriseFeeCalculator.any_instance.should_receive(:fees_for).with(variant) { 23 } + variant.fees_for(distributor, order_cycle).should == 23 end end + describe "calculating fees broken down by fee type" do + it "delegates to EnterpriseFeeCalculator" do + distributor = double(:distributor) + order_cycle = double(:order_cycle) + variant = Variant.new + fees = double(:fees) + + OpenFoodNetwork::EnterpriseFeeCalculator.any_instance.should_receive(:fees_by_type_for).with(variant) { fees } + + variant.fees_by_type_for(distributor, order_cycle).should == fees + end + end + + context "when the product has variants" do let!(:product) { create(:simple_product) } let!(:variant) { create(:variant, product: product) } diff --git a/spec/support/request/ui_component_helper.rb b/spec/support/request/ui_component_helper.rb index 7ca025e235..d9f01b447b 100644 --- a/spec/support/request/ui_component_helper.rb +++ b/spec/support/request/ui_component_helper.rb @@ -68,6 +68,10 @@ module UIComponentHelper find("#cart").click end + def cart_dirty + page.find("span.cart-span")[:class].include? 'dirty' + end + def wait_for_ajax counter = 0 while page.execute_script("return $.active").to_i > 0