mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Auto-merge from CI [skip ci]
This commit is contained in:
@@ -1,18 +1,69 @@
|
||||
Darkswarm.controller "EnterprisesCtrl", ($scope, Enterprises, Search, $document, $rootScope, HashNavigation, FilterSelectorsService, EnterpriseModal) ->
|
||||
Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, Enterprises, Search, $document, HashNavigation, FilterSelectorsService, EnterpriseModal, enterpriseMatchesNameQueryFilter, distanceWithinKmFilter) ->
|
||||
$scope.Enterprises = Enterprises
|
||||
$scope.totalActive = FilterSelectorsService.totalActive
|
||||
$scope.clearAll = FilterSelectorsService.clearAll
|
||||
$scope.filterText = FilterSelectorsService.filterText
|
||||
$scope.FilterSelectorsService = FilterSelectorsService
|
||||
$scope.totalActive = FilterSelectorsService.totalActive
|
||||
$scope.clearAll = FilterSelectorsService.clearAll
|
||||
$scope.filterText = FilterSelectorsService.filterText
|
||||
$scope.FilterSelectorsService = FilterSelectorsService
|
||||
$scope.query = Search.search()
|
||||
$scope.openModal = EnterpriseModal.open
|
||||
$scope.activeTaxons = []
|
||||
$scope.show_profiles = false
|
||||
$scope.filtersActive = false
|
||||
$scope.distanceMatchesShown = false
|
||||
|
||||
|
||||
$scope.$watch "query", (query)->
|
||||
Enterprises.flagMatching query
|
||||
Search.search query
|
||||
$rootScope.$broadcast 'enterprisesChanged'
|
||||
$scope.distanceMatchesShown = false
|
||||
|
||||
$timeout ->
|
||||
Enterprises.calculateDistance query, $scope.firstNameMatch()
|
||||
$rootScope.$broadcast 'enterprisesChanged'
|
||||
|
||||
|
||||
$rootScope.$on "enterprisesChanged", ->
|
||||
$scope.filterEnterprises()
|
||||
$scope.updateVisibleMatches()
|
||||
|
||||
|
||||
# When filter settings change, this could change which name match is at the top, or even
|
||||
# result in no matches. This affects the reference point that the distance matches are
|
||||
# calculated from, so we need to recalculate distances.
|
||||
$scope.$watch '[activeTaxons, shippingTypes, show_profiles]', ->
|
||||
$timeout ->
|
||||
Enterprises.calculateDistance $scope.query, $scope.firstNameMatch()
|
||||
$rootScope.$broadcast 'enterprisesChanged'
|
||||
, true
|
||||
|
||||
|
||||
$rootScope.$on "$locationChangeSuccess", (newRoute, oldRoute) ->
|
||||
if HashNavigation.active "hubs"
|
||||
$document.scrollTo $("#hubs"), 100, 200
|
||||
|
||||
|
||||
$scope.filterEnterprises = ->
|
||||
es = Enterprises.hubs
|
||||
$scope.nameMatches = enterpriseMatchesNameQueryFilter(es, true)
|
||||
$scope.distanceMatches = enterpriseMatchesNameQueryFilter(es, false)
|
||||
$scope.distanceMatches = distanceWithinKmFilter($scope.distanceMatches, 50)
|
||||
|
||||
|
||||
$scope.updateVisibleMatches = ->
|
||||
$scope.visibleMatches = if $scope.nameMatches.length == 0 || $scope.distanceMatchesShown
|
||||
$scope.nameMatches.concat $scope.distanceMatches
|
||||
else
|
||||
$scope.nameMatches
|
||||
|
||||
|
||||
$scope.showDistanceMatches = ->
|
||||
$scope.distanceMatchesShown = true
|
||||
$scope.updateVisibleMatches()
|
||||
|
||||
|
||||
$scope.firstNameMatch = ->
|
||||
if $scope.nameMatchesFiltered?
|
||||
$scope.nameMatchesFiltered[0]
|
||||
else
|
||||
undefined
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
Darkswarm.controller "LineItemCtrl", ($scope)->
|
||||
$scope.$watch "line_item.quantity", (newValue, oldValue)->
|
||||
$scope.$watch '[line_item.quantity, line_item.max_quantity]', (newValue, oldValue)->
|
||||
if newValue != oldValue
|
||||
$scope.Cart.orderChanged()
|
||||
, true
|
||||
@@ -0,0 +1,5 @@
|
||||
Darkswarm.filter 'distanceWithinKm', ->
|
||||
(enterprises, range) ->
|
||||
enterprises ||= []
|
||||
enterprises.filter (enterprise) ->
|
||||
enterprise.distance / 1000 <= range
|
||||
@@ -0,0 +1,4 @@
|
||||
Darkswarm.filter 'enterpriseMatchesNameQuery', ->
|
||||
(enterprises, matches_name_query) ->
|
||||
enterprises.filter (enterprise) ->
|
||||
enterprise.matches_name_query == matches_name_query
|
||||
@@ -20,7 +20,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
|
||||
$http.post('/orders/populate', @data()).success (data, status)=>
|
||||
@saved()
|
||||
.error (response, status)=>
|
||||
# TODO what shall we do here?
|
||||
@scheduleRetry()
|
||||
|
||||
data: =>
|
||||
variants = {}
|
||||
@@ -30,6 +30,12 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
|
||||
max_quantity: li.max_quantity
|
||||
{variants: variants}
|
||||
|
||||
scheduleRetry: =>
|
||||
console.log "Error updating cart: #{status}. Retrying in 3 seconds..."
|
||||
$timeout =>
|
||||
console.log "Retrying cart update"
|
||||
@orderChanged()
|
||||
, 3000
|
||||
|
||||
saved: =>
|
||||
@dirty = false
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter)->
|
||||
Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope)->
|
||||
new class Enterprises
|
||||
enterprises_by_id: {}
|
||||
constructor: ->
|
||||
@@ -28,3 +28,36 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer,
|
||||
Dereferencer.dereference enterprise.taxons, Taxons.taxons_by_id
|
||||
Dereferencer.dereference enterprise.supplied_taxons, Taxons.taxons_by_id
|
||||
|
||||
flagMatching: (query) ->
|
||||
for enterprise in @enterprises
|
||||
enterprise.matches_name_query = if query? && query.length > 0
|
||||
Matcher.match([enterprise.name], query)
|
||||
else
|
||||
false
|
||||
|
||||
calculateDistance: (query, firstMatching) ->
|
||||
if query?.length > 0
|
||||
if firstMatching?
|
||||
@setDistanceFrom firstMatching
|
||||
else
|
||||
@calculateDistanceGeo query
|
||||
else
|
||||
@resetDistance()
|
||||
|
||||
calculateDistanceGeo: (query) ->
|
||||
Geo.geocode query, (results, status) =>
|
||||
$rootScope.$apply =>
|
||||
if status == Geo.OK
|
||||
#console.log "Geocoded #{query} -> #{results[0].geometry.location}."
|
||||
@setDistanceFrom results[0].geometry.location
|
||||
else
|
||||
console.log "Geocoding failed for the following reason: #{status}"
|
||||
@resetDistance()
|
||||
|
||||
setDistanceFrom: (locatable) ->
|
||||
for enterprise in @enterprises
|
||||
enterprise.distance = Geo.distanceBetween enterprise, locatable
|
||||
$rootScope.$broadcast 'enterprisesChanged'
|
||||
|
||||
resetDistance: ->
|
||||
enterprise.distance = null for enterprise in @enterprises
|
||||
|
||||
23
app/assets/javascripts/darkswarm/services/geo.js.erb.coffee
Normal file
23
app/assets/javascripts/darkswarm/services/geo.js.erb.coffee
Normal file
@@ -0,0 +1,23 @@
|
||||
Darkswarm.service "Geo", ->
|
||||
new class Geo
|
||||
OK: google.maps.GeocoderStatus.OK
|
||||
|
||||
# Usage:
|
||||
# Geo.geocode address, (results, status) ->
|
||||
# if status == Geo.OK
|
||||
# console.log results[0].geometry.location
|
||||
# else
|
||||
# console.log "Error: #{status}"
|
||||
geocode: (address, callback) ->
|
||||
geocoder = new google.maps.Geocoder()
|
||||
geocoder.geocode {'address': address, 'region': "<%= Spree::Country.find_by_id(Spree::Config[:default_country_id]).iso %>"}, callback
|
||||
|
||||
distanceBetween: (src, dst) ->
|
||||
google.maps.geometry.spherical.computeDistanceBetween @toLatLng(src), @toLatLng(dst)
|
||||
|
||||
# Wrap an object in a google.maps.LatLng if it has not been already
|
||||
toLatLng: (locatable) ->
|
||||
if locatable.lat?
|
||||
locatable
|
||||
else
|
||||
new google.maps.LatLng locatable.latitude, locatable.longitude
|
||||
0
app/assets/javascripts/shared/ng-tags-input.min.js
vendored
Executable file → Normal file
0
app/assets/javascripts/shared/ng-tags-input.min.js
vendored
Executable file → Normal file
@@ -55,7 +55,7 @@
|
||||
100%
|
||||
opacity: 1
|
||||
|
||||
@-webkit-keyframes spin
|
||||
@-webkit-keyframes spin
|
||||
0%
|
||||
-webkit-transform: rotate(0deg)
|
||||
transform: rotate(0deg)
|
||||
@@ -104,12 +104,10 @@
|
||||
|
||||
|
||||
.animate-repeat
|
||||
-webkit-transform: translateZ(0)
|
||||
transform: translateZ(0)
|
||||
&.ng-move, &.ng-enter, &.ng-leave
|
||||
-webkit-transition: all 300ms linear
|
||||
transition: all 300ms linear
|
||||
|
||||
-webkit-transition: all 300ms linear
|
||||
transition: all 300ms linear
|
||||
|
||||
&.ng-leave
|
||||
opacity: 1
|
||||
&.ng-leave-active
|
||||
@@ -178,7 +176,7 @@ product.animate-repeat
|
||||
overflow: hidden
|
||||
max-height: 0
|
||||
opacity: 0 !important
|
||||
|
||||
|
||||
// &.ng-hide-add-active, &.ng-hide-remove-active
|
||||
|
||||
&.ng-hide-add, &.ng-hide-remove
|
||||
@@ -197,7 +195,7 @@ product.animate-repeat
|
||||
|
||||
&.ng-hide
|
||||
opacity: 0 !important
|
||||
|
||||
|
||||
// &.ng-hide-add-active, &.ng-hide-remove-active
|
||||
|
||||
&.ng-hide-add, &.ng-hide-remove
|
||||
@@ -206,8 +204,8 @@ product.animate-repeat
|
||||
it as hidden. */
|
||||
display: block !important
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@mixin csstrans
|
||||
@@ -217,7 +215,3 @@ product.animate-repeat
|
||||
-o-transition: all 300ms ease
|
||||
transition: all 300ms ease
|
||||
-webkit-transform-style: preserve-3d
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,3 +5,6 @@
|
||||
background-color: lighten($ofn-grey, 43%)
|
||||
@include panepadding
|
||||
@include sidepaddingSm
|
||||
|
||||
.name-matches, .distance-matches
|
||||
margin-top: 4em
|
||||
@@ -6,6 +6,7 @@ require 'open_food_network/order_grouper'
|
||||
require 'open_food_network/customers_report'
|
||||
require 'open_food_network/users_and_enterprises_report'
|
||||
require 'open_food_network/order_cycle_management_report'
|
||||
require 'open_food_network/packing_report'
|
||||
require 'open_food_network/sales_tax_report'
|
||||
require 'open_food_network/xero_invoices_report'
|
||||
|
||||
@@ -31,11 +32,15 @@ Spree::Admin::ReportsController.class_eval do
|
||||
order_cycle_management: [
|
||||
["Payment Methods Report", :payment_methods],
|
||||
["Delivery Report", :delivery]
|
||||
],
|
||||
packing: [
|
||||
["Pack By Customer", :pack_by_customer],
|
||||
["Pack By Supplier", :pack_by_supplier]
|
||||
]
|
||||
}
|
||||
|
||||
# Fetches user's distributors, suppliers and order_cycles
|
||||
before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management]
|
||||
before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management, :packing]
|
||||
|
||||
# Render a partial for orders and fulfillment description
|
||||
respond_override :index => { :html => { :success => lambda {
|
||||
@@ -47,7 +52,9 @@ Spree::Admin::ReportsController.class_eval do
|
||||
render_to_string(partial: 'customers_description', layout: false, locals: {report_types: REPORT_TYPES[:customers]}).html_safe
|
||||
@reports[:order_cycle_management][:description] =
|
||||
render_to_string(partial: 'order_cycle_management_description', layout: false, locals: {report_types: REPORT_TYPES[:order_cycle_management]}).html_safe
|
||||
} } }
|
||||
@reports[:packing][:description] =
|
||||
render_to_string(partial: 'packing_description', layout: false, locals: {report_types: REPORT_TYPES[:packing]}).html_safe
|
||||
} } }
|
||||
|
||||
|
||||
# Overide spree reports list.
|
||||
@@ -75,6 +82,40 @@ Spree::Admin::ReportsController.class_eval do
|
||||
render_report(@report.header, @report.table, params[:csv], "order_cycle_management_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def packing
|
||||
# -- Prepare parameters
|
||||
params[:q] ||= {}
|
||||
if params[:q][:completed_at_gt].blank?
|
||||
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
|
||||
else
|
||||
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]) rescue Time.zone.now.beginning_of_month
|
||||
end
|
||||
if params[:q] && !params[:q][:completed_at_lt].blank?
|
||||
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue ""
|
||||
end
|
||||
params[:q][:meta_sort] ||= "completed_at.desc"
|
||||
|
||||
# -- Prepare form options
|
||||
my_distributors = Enterprise.is_distributor.managed_by(spree_current_user)
|
||||
my_suppliers = Enterprise.is_primary_producer.managed_by(spree_current_user)
|
||||
|
||||
# My distributors and any distributors distributing products I supply
|
||||
@distributors = my_distributors | Enterprise.with_distributed_products_outer.merge(Spree::Product.in_any_supplier(my_suppliers))
|
||||
# My suppliers and any suppliers supplying products I distribute
|
||||
@suppliers = my_suppliers | my_distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq
|
||||
@order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC')
|
||||
@report_types = REPORT_TYPES[:packing]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::PackingReport.new spree_current_user, params
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
|
||||
@table = order_grouper.table(@report.table_items)
|
||||
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], "packing_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def orders_and_distributors
|
||||
params[:q] ||= {}
|
||||
|
||||
@@ -732,9 +773,9 @@ Spree::Admin::ReportsController.class_eval do
|
||||
:sales_total => { :name => "Sales Total", :description => "Sales Total For All Orders" },
|
||||
:users_and_enterprises => { :name => "Users & Enterprises", :description => "Enterprise Ownership & Status" },
|
||||
:order_cycle_management => {:name => "Order Cycle Management", :description => ''},
|
||||
:packing => {:name => "Packing Reports", :description => ''},
|
||||
:sales_tax => { :name => "Sales Tax", :description => "Sales Tax For Orders" },
|
||||
:xero_invoices => { :name => "Xero Invoices", :description => 'Invoices for import into Xero' }
|
||||
|
||||
}
|
||||
# Return only reports the user is authorized to view.
|
||||
reports.select { |action| can? action, :report }
|
||||
|
||||
@@ -123,7 +123,7 @@ class AbilityDecorator
|
||||
can [:admin, :index, :read, :create, :edit], Spree::Classification
|
||||
|
||||
# Reports page
|
||||
can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], :report
|
||||
can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :packing], :report
|
||||
end
|
||||
|
||||
def add_order_cycle_management_abilities(user)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"}
|
||||
.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}", bindonce: true}
|
||||
.columns.small-12.medium-6.large-5.fat
|
||||
%div{"bo-if" => "hub.taxons"}
|
||||
%label Shop for
|
||||
.trans-sentence
|
||||
%span.fat-taxons{"ng-repeat" => "taxon in hub.taxons"}
|
||||
%render-svg{path: "{{taxon.icon}}"}
|
||||
%span{"bo-text" => "taxon.name"}
|
||||
%span{"bo-text" => "taxon.name"}
|
||||
%div.show-for-medium-up{"bo-if" => "hub.taxons.length==0"}
|
||||
|
||||
.columns.small-12.medium-3.large-2.fat
|
||||
%div{"bo-if" => "hub.pickup || hub.delivery"}
|
||||
%label Delivery options
|
||||
%ul.small-block-grid-2.medium-block-grid-1.large-block-grid-1
|
||||
%li.pickup{"bo-if" => "hub.pickup"}
|
||||
%li.pickup{"bo-if" => "hub.pickup"}
|
||||
%i.ofn-i_038-takeaway
|
||||
Pickup
|
||||
%li.delivery{"bo-if" => "hub.delivery"}
|
||||
%li.delivery{"bo-if" => "hub.delivery"}
|
||||
%i.ofn-i_039-delivery
|
||||
Delivery
|
||||
.columns.small-12.medium-3.large-5.fat
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
%h5.tdhead
|
||||
.light Filter by
|
||||
Type
|
||||
%filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{ objects: "Enterprises.hubs | searchEnterprises:query | taxonsOf", "active-selectors" => "activeTaxons" }
|
||||
%filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{ objects: "visibleMatches | visible | taxonsOf", "active-selectors" => "activeTaxons" }
|
||||
.small-12.large-3.columns
|
||||
%h5.tdhead
|
||||
.light Filter by
|
||||
|
||||
@@ -5,18 +5,22 @@
|
||||
.small-12.columns
|
||||
%h1{"scroll-after-load" => (spree_current_user ? true : nil)} Shop in your local area
|
||||
|
||||
= render partial: "shared/components/enterprise_search"
|
||||
= render partial: "home/filters"
|
||||
= render "shared/components/enterprise_search"
|
||||
= render "home/filters"
|
||||
|
||||
.row{bindonce: true}
|
||||
.row
|
||||
.small-12.columns
|
||||
.active_table
|
||||
%hub.active_table_node.row.animate-repeat{"ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])",
|
||||
"ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}",
|
||||
"ng-controller" => "HubNodeCtrl",
|
||||
id: "{{hub.hash}}"}
|
||||
.small-12.columns
|
||||
= render partial: 'home/skinny'
|
||||
= render partial: 'home/fat'
|
||||
.name-matches{"ng-show" => "nameMatchesFiltered.length > 0"}
|
||||
%h2 Did you mean?
|
||||
= render "home/hubs_table", enterprises: "nameMatches"
|
||||
|
||||
= render partial: 'shared/components/enterprise_no_results'
|
||||
.distance-matches{"ng-if" => "nameMatchesFiltered.length == 0 || distanceMatchesShown"}
|
||||
%h2{"ng-show" => "nameMatchesFiltered.length > 0 || query.length > 0"}
|
||||
Closest to
|
||||
%span{"ng-show" => "nameMatchesFiltered.length > 0"} {{ nameMatchesFiltered[0].name }}...
|
||||
%span{"ng-hide" => "nameMatchesFiltered.length > 0"} {{ query }}...
|
||||
|
||||
= render "home/hubs_table", enterprises: "distanceMatches"
|
||||
|
||||
.show-distance-matches{"ng-show" => "nameMatchesFiltered.length > 0 && !distanceMatchesShown"}
|
||||
%a{href: "", "ng-click" => "showDistanceMatches()"} Show me shops near {{ nameMatchesFiltered[0].name }}
|
||||
|
||||
10
app/views/home/_hubs_table.html.haml
Normal file
10
app/views/home/_hubs_table.html.haml
Normal file
@@ -0,0 +1,10 @@
|
||||
.active_table
|
||||
%hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | visible | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])",
|
||||
"ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}",
|
||||
"ng-controller" => "HubNodeCtrl",
|
||||
id: "{{hub.hash}}"}
|
||||
.small-12.columns
|
||||
= render 'home/skinny'
|
||||
= render 'home/fat'
|
||||
|
||||
= render 'shared/components/enterprise_no_results', enterprises: "#{enterprises}Filtered"
|
||||
@@ -1,5 +1,4 @@
|
||||
.row.active_table_row{"ng-if" => "hub.is_distributor", "ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}", bindonce: true}
|
||||
|
||||
.columns.small-12.medium-5.large-5.skinny-head
|
||||
%a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub", "data-is-link" => "true"}
|
||||
%i{bo: {class: "hub.icon_font"}}
|
||||
@@ -9,6 +8,7 @@
|
||||
%span.margin-top{"bo-text" => "hub.address.city"}
|
||||
.columns.small-2.medium-1.large-1
|
||||
%span.margin-top{"bo-bind" => "hub.address.state_name | uppercase"}
|
||||
%span.margin-top{"ng-if" => "hub.distance != null && hub.distance > 0"} ({{ hub.distance / 1000 | number:0 }} km)
|
||||
|
||||
.columns.small-4.medium-3.large-3.text-right{"bo-if" => "hub.active"}
|
||||
%a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"}
|
||||
@@ -26,10 +26,10 @@
|
||||
%span.margin-top{ bo: { if: "!current()" } } Orders closed
|
||||
|
||||
.columns.small-2.medium-1.large-1.text-right
|
||||
%span.margin-top
|
||||
%span.margin-top
|
||||
%i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"}
|
||||
|
||||
.row.active_table_row{"ng-if" => "!hub.is_distributor", "ng-class" => "closed"}
|
||||
.row.active_table_row{"ng-if" => "!hub.is_distributor", "ng-class" => "closed", bindonce: true}
|
||||
.columns.small-12.medium-6.large-5.skinny-head
|
||||
%a.hub{"ng-click" => "openModal(hub)", "ng-class" => "{primary: hub.active, secondary: !hub.active}"}
|
||||
%i{ng: {class: "hub.icon_font"}}
|
||||
@@ -43,4 +43,3 @@
|
||||
.columns.small-6.medium-3.large-4.text-right
|
||||
%span.margin-top{ bo: { if: "!current()" } }
|
||||
%em Profile only
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
%link{href: "https://fonts.googleapis.com/css?family=Roboto:400,300italic,400italic,300,700,700italic|Oswald:300,400,700", rel: "stylesheet", type: "text/css"}
|
||||
|
||||
= yield :scripts
|
||||
%script{src: "//maps.googleapis.com/maps/api/js?libraries=places&sensor=false"}
|
||||
%script{src: "//maps.googleapis.com/maps/api/js?libraries=places,geometry&sensor=false"}
|
||||
= split_stylesheet_link_tag "darkswarm/all"
|
||||
= javascript_include_tag "darkswarm/all"
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%producer.row{"ng-show" => "filteredEnterprises.length == 0"}
|
||||
- enterprises ||= 'filteredEnterprises'
|
||||
%producer.row{"ng-show" => "#{enterprises}.length == 0"}
|
||||
%p.no-results
|
||||
Sorry, no results found for
|
||||
%strong {{query}}.
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
%input{type: :text,
|
||||
"ng-model" => "query",
|
||||
placeholder: t('search_by_name'),
|
||||
"ng-debounce" => "150",
|
||||
"ng-debounce" => "500",
|
||||
"ofn-disable-enter" => true}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
%ul{style: "margin-left: 12pt"}
|
||||
- report_types.each do |report_type|
|
||||
%li
|
||||
= link_to report_type[0], "#{packing_admin_reports_url}?report_type=#{report_type[1]}"
|
||||
42
app/views/spree/admin/reports/packing.html.haml
Normal file
42
app/views/spree/admin/reports/packing.html.haml
Normal file
@@ -0,0 +1,42 @@
|
||||
= form_for @report.search, :url => spree.packing_admin_reports_path do |f|
|
||||
= render 'date_range_form', f: f
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, "Hubs: "
|
||||
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, "Producers: "
|
||||
.omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true})
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, "Order Cycles: "
|
||||
.omega.fourteen.columns
|
||||
= f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true})
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, "Report Type: "
|
||||
.omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, @report_type))
|
||||
|
||||
.row
|
||||
= check_box_tag :csv
|
||||
= label_tag :csv, "Download as csv"
|
||||
|
||||
.row
|
||||
= button t(:search)
|
||||
|
||||
%br
|
||||
%br
|
||||
%table#listing_orders.index
|
||||
%thead
|
||||
%tr{'data-hook' => "orders_header"}
|
||||
- @report.header.each do |heading|
|
||||
%th=heading
|
||||
%tbody
|
||||
- @table.each do |row|
|
||||
%tr
|
||||
- row.each do |column|
|
||||
%td= column
|
||||
- if @table.empty?
|
||||
%tr
|
||||
%td{:colspan => "2"}= t(:none)
|
||||
@@ -145,6 +145,7 @@ end
|
||||
Spree::Core::Engine.routes.prepend do
|
||||
match '/admin/reports/orders_and_distributors' => 'admin/reports#orders_and_distributors', :as => "orders_and_distributors_admin_reports", :via => [:get, :post]
|
||||
match '/admin/reports/order_cycle_management' => 'admin/reports#order_cycle_management', :as => "order_cycle_management_admin_reports", :via => [:get, :post]
|
||||
match '/admin/reports/packing' => 'admin/reports#packing', :as => "packing_admin_reports", :via => [:get, :post]
|
||||
match '/admin/reports/group_buys' => 'admin/reports#group_buys', :as => "group_buys_admin_reports", :via => [:get, :post]
|
||||
match '/admin/reports/bulk_coop' => 'admin/reports#bulk_coop', :as => "bulk_coop_admin_reports", :via => [:get, :post]
|
||||
match '/admin/reports/payments' => 'admin/reports#payments', :as => "payments_admin_reports", :via => [:get, :post]
|
||||
|
||||
124
lib/open_food_network/packing_report.rb
Normal file
124
lib/open_food_network/packing_report.rb
Normal file
@@ -0,0 +1,124 @@
|
||||
module OpenFoodNetwork
|
||||
class PackingReport
|
||||
attr_reader :params
|
||||
def initialize(user, params = {})
|
||||
@params = params
|
||||
@user = user
|
||||
end
|
||||
|
||||
def header
|
||||
if is_by_customer?
|
||||
["Hub", "Code", "First Name", "Last Name", "Supplier", "Product", "Variant", "Quantity", "TempControlled?"]
|
||||
else
|
||||
["Hub", "Supplier", "Code", "First Name", "Last Name", "Product", "Variant", "Quantity", "TempControlled?"]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
Spree::Order.complete.not_state(:canceled).managed_by(@user).search(params[:q])
|
||||
end
|
||||
|
||||
def orders
|
||||
search.result
|
||||
end
|
||||
|
||||
def table_items
|
||||
@line_items = orders.map do |o|
|
||||
lis = o.line_items.managed_by(@user)
|
||||
lis = lis.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present?
|
||||
lis
|
||||
end.flatten
|
||||
end
|
||||
|
||||
def rules
|
||||
if is_by_customer?
|
||||
# customer_rows orders
|
||||
# table_items = @line_items
|
||||
|
||||
[
|
||||
{ group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |line_item| line_item.order },
|
||||
sort_by: proc { |order| order.bill_address.lastname },
|
||||
summary_columns: [ proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "TOTAL ITEMS" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| "" } ] },
|
||||
{ group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.product.name } } ]
|
||||
else
|
||||
# supplier_rows orders
|
||||
# table_items = supplier_rows orders
|
||||
#
|
||||
[ { group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name },
|
||||
summary_columns: [ proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "TOTAL ITEMS" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| "" } ] },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.product.name } } ]
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
if is_by_customer?
|
||||
[ proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| customer_code(line_items.first.order.email) },
|
||||
proc { |line_items| line_items.first.order.bill_address.firstname },
|
||||
proc { |line_items| line_items.first.order.bill_address.lastname },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| is_temperature_controlled?(line_items.first) }
|
||||
]
|
||||
else
|
||||
[
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| customer_code(line_items.first.order.email) },
|
||||
proc { |line_items| line_items.first.order.bill_address.firstname },
|
||||
proc { |line_items| line_items.first.order.bill_address.lastname },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| is_temperature_controlled?(line_items.first) }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def is_temperature_controlled?(line_item)
|
||||
if line_item.product.shipping_category.andand.temperature_controlled
|
||||
"Yes"
|
||||
else
|
||||
"No"
|
||||
end
|
||||
end
|
||||
|
||||
def is_by_customer?
|
||||
params[:report_type] == "pack_by_customer"
|
||||
end
|
||||
|
||||
def customer_code(email)
|
||||
customer = Customer.where(email: email).first
|
||||
customer.nil? ? "" : customer.code
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -25,6 +25,16 @@ module OpenFoodNetwork
|
||||
@variant_override.andand.count_on_hand || super
|
||||
end
|
||||
|
||||
def on_demand
|
||||
if @variant_override.andand.count_on_hand.present?
|
||||
# If we're overriding the stock level of an on_demand variant, show it as not
|
||||
# on_demand, so our stock control can take effect.
|
||||
false
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def decrement!(attribute, by=1)
|
||||
if attribute == :count_on_hand && @variant_override.andand.stock_overridden?
|
||||
@variant_override.decrement_stock! by
|
||||
|
||||
@@ -84,6 +84,66 @@ feature %q{
|
||||
end
|
||||
end
|
||||
|
||||
describe "Packing reports" do
|
||||
before do
|
||||
login_to_admin_section
|
||||
click_link "Reports"
|
||||
end
|
||||
|
||||
let(:bill_address1) { create(:address, lastname: "Aman") }
|
||||
let(:bill_address2) { create(:address, lastname: "Bman") }
|
||||
let(:distributor_address) { create(:address, :address1 => "distributor address", :city => 'The Shire', :zipcode => "1234") }
|
||||
let(:distributor) { create(:distributor_enterprise, :address => distributor_address) }
|
||||
let(:order1) { create(:order, distributor: distributor, bill_address: bill_address1) }
|
||||
let(:order2) { create(:order, distributor: distributor, bill_address: bill_address2) }
|
||||
let(:supplier) { create(:supplier_enterprise, name: "Supplier") }
|
||||
let(:product_1) { create(:simple_product, name: "Product 1", supplier: supplier ) }
|
||||
let(:variant_1) { create(:variant, product: product_1, unit_description: "Big") }
|
||||
let(:variant_2) { create(:variant, product: product_1, unit_description: "Small") }
|
||||
let(:product_2) { create(:simple_product, name: "Product 2", supplier: supplier) }
|
||||
|
||||
before do
|
||||
Timecop.travel(Time.zone.local(2013, 4, 25, 14, 0, 0)) { order1.finalize! }
|
||||
Timecop.travel(Time.zone.local(2013, 4, 25, 15, 0, 0)) { order2.finalize! }
|
||||
|
||||
create(:line_item, variant: variant_1, quantity: 1, order: order1)
|
||||
create(:line_item, variant: variant_2, quantity: 3, order: order1)
|
||||
create(:line_item, variant: product_2.master, quantity: 3, order: order2)
|
||||
|
||||
end
|
||||
|
||||
scenario "Pack By Customer" do
|
||||
click_link "Pack By Customer"
|
||||
fill_in 'q_completed_at_gt', with: '2013-04-25 13:00:00'
|
||||
fill_in 'q_completed_at_lt', with: '2013-04-25 16:00:00'
|
||||
#select 'Pack By Customer', from: 'report_type'
|
||||
click_button 'Search'
|
||||
|
||||
rows = find("table#listing_orders.index").all("thead tr")
|
||||
table = rows.map { |r| r.all("th").map { |c| c.text.strip } }
|
||||
table.sort.should == [
|
||||
["Hub", "Code", "First Name", "Last Name", "Supplier", "Product", "Variant", "Quantity", "TempControlled?"]
|
||||
].sort
|
||||
all('table#listing_orders tbody tr').count.should == 5 # Totals row per order
|
||||
end
|
||||
|
||||
scenario "Pack By Supplier" do
|
||||
click_link "Pack By Supplier"
|
||||
fill_in 'q_completed_at_gt', with: '2013-04-25 13:00:00'
|
||||
fill_in 'q_completed_at_lt', with: '2013-04-25 16:00:00'
|
||||
#select 'Pack By Customer', from: 'report_type'
|
||||
click_button 'Search'
|
||||
|
||||
rows = find("table#listing_orders").all("thead tr")
|
||||
table = rows.map { |r| r.all("th").map { |c| c.text.strip } }
|
||||
table.sort.should == [
|
||||
["Hub", "Supplier", "Code", "First Name", "Last Name", "Product", "Variant", "Quantity", "TempControlled?"]
|
||||
].sort
|
||||
all('table#listing_orders tbody tr').count.should == 4 # Totals row per supplier
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
scenario "orders and distributors report" do
|
||||
login_to_admin_section
|
||||
click_link 'Reports'
|
||||
|
||||
@@ -134,15 +134,20 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
end
|
||||
|
||||
it "should save group buy data to the cart" do
|
||||
# -- Quantity
|
||||
fill_in "variants[#{variant.id}]", with: 6
|
||||
fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 7
|
||||
page.should have_in_cart product.name
|
||||
wait_until { !cart_dirty }
|
||||
|
||||
li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last
|
||||
li.quantity.should == 6
|
||||
|
||||
# -- Max quantity
|
||||
fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 7
|
||||
wait_until { !cart_dirty }
|
||||
|
||||
li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last
|
||||
li.max_quantity.should == 7
|
||||
li.quantity.should == 6
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,18 +15,23 @@ feature "shopping with variant overrides defined", js: true do
|
||||
let(:pm) { hub.payment_methods.first }
|
||||
let(:p1) { create(:simple_product, supplier: producer) }
|
||||
let(:p2) { create(:simple_product, supplier: producer) }
|
||||
let(:p3) { create(:simple_product, supplier: producer, on_demand: true) }
|
||||
let(:v1) { create(:variant, product: p1, price: 11.11, unit_value: 1) }
|
||||
let(:v2) { create(:variant, product: p1, price: 22.22, unit_value: 2) }
|
||||
let(:v3) { create(:variant, product: p2, price: 33.33, unit_value: 3) }
|
||||
let(:v4) { create(:variant, product: p1, price: 44.44, unit_value: 4) }
|
||||
let(:v5) { create(:variant, product: p3, price: 55.55, unit_value: 5, on_demand: true) }
|
||||
let(:v6) { create(:variant, product: p3, price: 66.66, unit_value: 6, on_demand: true) }
|
||||
let!(:vo1) { create(:variant_override, hub: hub, variant: v1, price: 55.55, count_on_hand: nil) }
|
||||
let!(:vo2) { create(:variant_override, hub: hub, variant: v2, count_on_hand: 0) }
|
||||
let!(:vo3) { create(:variant_override, hub: hub, variant: v3, count_on_hand: 0) }
|
||||
let!(:vo4) { create(:variant_override, hub: hub, variant: v4, count_on_hand: 3) }
|
||||
let!(:vo5) { create(:variant_override, hub: hub, variant: v5, count_on_hand: 0) }
|
||||
let!(:vo6) { create(:variant_override, hub: hub, variant: v6, count_on_hand: 6) }
|
||||
let(:ef) { create(:enterprise_fee, enterprise: hub, fee_type: 'packing', calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)) }
|
||||
|
||||
before do
|
||||
outgoing_exchange.variants = [v1, v2, v3, v4]
|
||||
outgoing_exchange.variants = [v1, v2, v3, v4, v5, v6]
|
||||
outgoing_exchange.enterprise_fees << ef
|
||||
sm.calculator.preferred_amount = 0
|
||||
visit shops_path
|
||||
@@ -46,6 +51,9 @@ feature "shopping with variant overrides defined", js: true do
|
||||
# Entire product should not appear - no stock
|
||||
page.should_not have_content p2.name
|
||||
page.should_not have_content v3.options_text
|
||||
|
||||
# On-demand product with VO of no stock should NOT appear
|
||||
page.should_not have_content v5.options_text
|
||||
end
|
||||
|
||||
it "calculates fees correctly" do
|
||||
@@ -127,6 +135,19 @@ feature "shopping with variant overrides defined", js: true do
|
||||
end.to change { vo4.reload.count_on_hand }.by(-2)
|
||||
end
|
||||
|
||||
it "subtracts stock from stock-overridden on_demand variants" do
|
||||
fill_in "variants[#{v6.id}]", with: "2"
|
||||
show_cart
|
||||
wait_until_enabled 'li.cart a.button'
|
||||
click_link 'Checkout now'
|
||||
|
||||
expect do
|
||||
expect do
|
||||
complete_checkout
|
||||
end.to change { v6.reload.count_on_hand }.by(0)
|
||||
end.to change { vo6.reload.count_on_hand }.by(-2)
|
||||
end
|
||||
|
||||
it "does not subtract stock from overrides that do not override count_on_hand" do
|
||||
fill_in "variants[#{v1.id}]", with: "2"
|
||||
show_cart
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
describe 'filtering by active', ->
|
||||
filterByActive = null
|
||||
objects = [
|
||||
{
|
||||
active: true
|
||||
}
|
||||
{
|
||||
active: false
|
||||
}
|
||||
{active: true}
|
||||
{active: false}
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
describe "filtering enterprises to those within a certain radius", ->
|
||||
filter = null
|
||||
enterprises = [
|
||||
{distance: 25000}
|
||||
{distance: 75000}
|
||||
]
|
||||
|
||||
beforeEach ->
|
||||
module 'Darkswarm'
|
||||
inject ($filter) ->
|
||||
filter = $filter('distanceWithinKm')
|
||||
|
||||
it "filters to those enterprises within a distance", ->
|
||||
expect(filter(enterprises, 50)).toEqual [enterprises[0]]
|
||||
|
||||
it "returns empty array when enterprises array is null", ->
|
||||
expect(filter(null, 50)).toEqual []
|
||||
@@ -3,6 +3,8 @@ describe 'Cart service', ->
|
||||
Variants = null
|
||||
variant = null
|
||||
order = null
|
||||
$httpBackend = null
|
||||
$timeout = null
|
||||
|
||||
beforeEach ->
|
||||
module 'Darkswarm'
|
||||
@@ -16,9 +18,11 @@ describe 'Cart service', ->
|
||||
]
|
||||
}
|
||||
angular.module('Darkswarm').value('currentOrder', order)
|
||||
inject ($injector)->
|
||||
inject ($injector, _$httpBackend_, _$timeout_)->
|
||||
Variants = $injector.get("Variants")
|
||||
Cart = $injector.get("Cart")
|
||||
$httpBackend = _$httpBackend_
|
||||
$timeout = _$timeout_
|
||||
|
||||
it "backreferences line items", ->
|
||||
expect(Cart.line_items[0].variant.line_item).toBe Cart.line_items[0]
|
||||
@@ -44,6 +48,29 @@ describe 'Cart service', ->
|
||||
order.line_items[0].quantity = 2
|
||||
expect(Cart.total_item_count()).toEqual 2
|
||||
|
||||
describe "updating the cart", ->
|
||||
data = {variants: {}}
|
||||
|
||||
it "marks the form as saved on success", ->
|
||||
spyOn(Cart, 'saved')
|
||||
$httpBackend.expectPOST("/orders/populate", data).respond 200, {}
|
||||
Cart.update()
|
||||
$httpBackend.flush()
|
||||
expect(Cart.saved).toHaveBeenCalled()
|
||||
|
||||
it "retries the update on failure", ->
|
||||
spyOn(Cart, 'scheduleRetry')
|
||||
$httpBackend.expectPOST("/orders/populate", data).respond 404, {}
|
||||
Cart.update()
|
||||
$httpBackend.flush()
|
||||
expect(Cart.scheduleRetry).toHaveBeenCalled()
|
||||
|
||||
it "schedules retries of updates", ->
|
||||
spyOn(Cart, 'orderChanged')
|
||||
Cart.scheduleRetry()
|
||||
$timeout.flush()
|
||||
expect(Cart.orderChanged).toHaveBeenCalled()
|
||||
|
||||
describe "generating an extended variant name", ->
|
||||
it "returns the product name when it is the same as the variant name", ->
|
||||
variant = {product_name: 'product_name', name_to_display: 'product_name'}
|
||||
|
||||
@@ -1,24 +1,37 @@
|
||||
describe "Enterprises service", ->
|
||||
Enterprises = null
|
||||
CurrentHubMock = {}
|
||||
Geo =
|
||||
OK: 'ok'
|
||||
succeed: true
|
||||
geocode: (query, callback) ->
|
||||
if @succeed
|
||||
results = [{geometry: {location: "location"}}]
|
||||
callback(results, @OK)
|
||||
else
|
||||
callback(results, 'Oops')
|
||||
distanceBetween: (locatable, location) ->
|
||||
123
|
||||
|
||||
taxons = [
|
||||
{id: 1, name: "test"}
|
||||
]
|
||||
enterprises = [
|
||||
{id: 1, visible: true, category: "hub", producers: [{id: 5}], taxons: [{id: 1}]},
|
||||
{id: 2, visible: true, category: "hub", producers: [{id: 6}]}
|
||||
{id: 3, visible: true, category: "hub_profile"}
|
||||
{id: 4, visible: false, category: "hub", producers: [{id: 7}]}
|
||||
{id: 5, visible: true, category: "producer_hub", hubs: [{id: 1}]},
|
||||
{id: 6, visible: true, category: "producer_shop", hubs: [{id: 2}]},
|
||||
{id: 7, visible: true, category: "producer", hubs: [{id: 2}]}
|
||||
{id: 8, visible: false, category: "producer", hubs: [{id: 2}]}
|
||||
{id: 1, visible: true, name: 'a', category: "hub", producers: [{id: 5}], taxons: [{id: 1}]},
|
||||
{id: 2, visible: true, name: 'b', category: "hub", producers: [{id: 6}]}
|
||||
{id: 3, visible: true, name: 'c', category: "hub_profile"}
|
||||
{id: 4, visible: false,name: 'd', category: "hub", producers: [{id: 7}]}
|
||||
{id: 5, visible: true, name: 'e', category: "producer_hub", hubs: [{id: 1}]},
|
||||
{id: 6, visible: true, name: 'f', category: "producer_shop", hubs: [{id: 2}]},
|
||||
{id: 7, visible: true, name: 'g', category: "producer", hubs: [{id: 2}]}
|
||||
{id: 8, visible: false,name: 'h', category: "producer", hubs: [{id: 2}]}
|
||||
]
|
||||
H1: 0
|
||||
beforeEach ->
|
||||
module 'Darkswarm'
|
||||
module ($provide)->
|
||||
$provide.value "CurrentHub", CurrentHubMock
|
||||
$provide.value "Geo", Geo
|
||||
null
|
||||
angular.module('Darkswarm').value('enterprises', enterprises)
|
||||
angular.module('Darkswarm').value('taxons', taxons)
|
||||
@@ -73,3 +86,70 @@ describe "Enterprises service", ->
|
||||
expect(Enterprises.producers).toContain Enterprises.enterprises[4]
|
||||
expect(Enterprises.producers).toContain Enterprises.enterprises[5]
|
||||
expect(Enterprises.producers).toContain Enterprises.enterprises[6]
|
||||
|
||||
describe "flagging enterprises with names matching a query", ->
|
||||
it "flags enterprises when a query is provided", ->
|
||||
Enterprises.flagMatching 'c'
|
||||
expect(e.matches_name_query).toBe true for e in enterprises when e.name == 'c'
|
||||
expect(e.matches_name_query).toBe false for e in enterprises when e.name != 'c'
|
||||
|
||||
it "clears flags when query is null", ->
|
||||
Enterprises.flagMatching null
|
||||
expect(e.matches_name_query).toBe false for e in enterprises
|
||||
|
||||
it "clears flags when query is blank", ->
|
||||
Enterprises.flagMatching ''
|
||||
expect(e.matches_name_query).toBe false for e in enterprises
|
||||
|
||||
describe "calculating the distance of enterprises from a location", ->
|
||||
describe "when a query is provided", ->
|
||||
it "sets the distance from the enterprise when a name match is available", ->
|
||||
spyOn(Enterprises, "setDistanceFrom")
|
||||
Enterprises.calculateDistance "asdf", 'match'
|
||||
expect(Enterprises.setDistanceFrom).toHaveBeenCalledWith('match')
|
||||
|
||||
it "calculates the distance from the geocoded query otherwise", ->
|
||||
spyOn(Enterprises, "calculateDistanceGeo")
|
||||
Enterprises.calculateDistance "asdf", undefined
|
||||
expect(Enterprises.calculateDistanceGeo).toHaveBeenCalledWith("asdf")
|
||||
|
||||
it "resets the distance when query is null", ->
|
||||
spyOn(Enterprises, "resetDistance")
|
||||
Enterprises.calculateDistance null
|
||||
expect(Enterprises.resetDistance).toHaveBeenCalled()
|
||||
|
||||
it "resets the distance when query is blank", ->
|
||||
spyOn(Enterprises, "resetDistance")
|
||||
Enterprises.calculateDistance ""
|
||||
expect(Enterprises.resetDistance).toHaveBeenCalled()
|
||||
|
||||
describe "calculating the distance of enterprises from a location by geocoding", ->
|
||||
beforeEach ->
|
||||
spyOn(Enterprises, "setDistanceFrom")
|
||||
|
||||
it "calculates distance for all enterprises when geocoding succeeds", ->
|
||||
Geo.succeed = true
|
||||
Enterprises.calculateDistanceGeo('query')
|
||||
expect(Enterprises.setDistanceFrom).toHaveBeenCalledWith("location")
|
||||
|
||||
it "resets distance when geocoding fails", ->
|
||||
Geo.succeed = false
|
||||
spyOn(Enterprises, "resetDistance")
|
||||
Enterprises.calculateDistanceGeo('query')
|
||||
expect(Enterprises.setDistanceFrom).not.toHaveBeenCalled()
|
||||
expect(Enterprises.resetDistance).toHaveBeenCalled()
|
||||
|
||||
describe "setting the distance of each enterprise from a central location", ->
|
||||
it "sets the distances", ->
|
||||
Enterprises.setDistanceFrom 'location'
|
||||
for e in Enterprises.enterprises
|
||||
expect(e.distance).toEqual 123
|
||||
|
||||
describe "resetting the distance measurement of all enterprises", ->
|
||||
beforeEach ->
|
||||
e.distance = 123 for e in Enterprises.enterprises
|
||||
|
||||
it "resets the distance", ->
|
||||
Enterprises.resetDistance()
|
||||
for e in Enterprises.enterprises
|
||||
expect(e.distance).toBeNull()
|
||||
@@ -1,7 +1,8 @@
|
||||
describe "Groups service", ->
|
||||
Groups = null
|
||||
Enterprises = null
|
||||
CurrentHubMock = {}
|
||||
CurrentHubMock = {}
|
||||
Geo = {}
|
||||
groups = [{
|
||||
id: 1
|
||||
name: "Test Group"
|
||||
@@ -17,17 +18,18 @@ describe "Groups service", ->
|
||||
|
||||
beforeEach ->
|
||||
module 'Darkswarm'
|
||||
angular.module('Darkswarm').value('groups', groups)
|
||||
angular.module('Darkswarm').value('enterprises', enterprises)
|
||||
angular.module('Darkswarm').value('groups', groups)
|
||||
angular.module('Darkswarm').value('enterprises', enterprises)
|
||||
module ($provide)->
|
||||
$provide.value "CurrentHub", CurrentHubMock
|
||||
$provide.value "CurrentHub", CurrentHubMock
|
||||
$provide.value "Geo", Geo
|
||||
null
|
||||
inject (_Groups_, _Enterprises_)->
|
||||
Groups = _Groups_
|
||||
Enterprises = _Enterprises_
|
||||
Groups = _Groups_
|
||||
Enterprises = _Enterprises_
|
||||
|
||||
it "dereferences group enterprises", ->
|
||||
expect(Groups.groups[0].enterprises[0]).toBe enterprises[0]
|
||||
|
||||
|
||||
it "dereferences enterprise groups", ->
|
||||
expect(Enterprises.enterprises[0].groups[0]).toBe groups[0]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
describe "Hubs service", ->
|
||||
OfnMap = null
|
||||
CurrentHubMock = {}
|
||||
CurrentHubMock = {}
|
||||
Geo = {}
|
||||
enterprises = [
|
||||
{
|
||||
id: 2
|
||||
@@ -13,12 +14,13 @@ describe "Hubs service", ->
|
||||
|
||||
beforeEach ->
|
||||
module 'Darkswarm'
|
||||
angular.module('Darkswarm').value('enterprises', enterprises)
|
||||
angular.module('Darkswarm').value('enterprises', enterprises)
|
||||
module ($provide)->
|
||||
$provide.value "CurrentHub", CurrentHubMock
|
||||
$provide.value "CurrentHub", CurrentHubMock
|
||||
$provide.value "Geo", Geo
|
||||
null
|
||||
inject ($injector)->
|
||||
OfnMap = $injector.get("OfnMap")
|
||||
OfnMap = $injector.get("OfnMap")
|
||||
|
||||
it "builds MapMarkers from enterprises", ->
|
||||
expect(OfnMap.enterprises[0].id).toBe enterprises[0].id
|
||||
|
||||
@@ -10,6 +10,7 @@ describe 'Products service', ->
|
||||
productWithImage = null
|
||||
properties = null
|
||||
taxons = null
|
||||
Geo = {}
|
||||
|
||||
beforeEach ->
|
||||
product =
|
||||
@@ -40,6 +41,7 @@ describe 'Products service', ->
|
||||
$provide.value "currentOrder", currentOrder
|
||||
$provide.value "taxons", taxons
|
||||
$provide.value "properties", properties
|
||||
$provide.value "Geo", Geo
|
||||
null
|
||||
|
||||
inject ($injector, _$httpBackend_)->
|
||||
|
||||
81
spec/lib/open_food_network/packing_report_spec.rb
Normal file
81
spec/lib/open_food_network/packing_report_spec.rb
Normal file
@@ -0,0 +1,81 @@
|
||||
require 'spec_helper'
|
||||
|
||||
include AuthenticationWorkflow
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe PackingReport do
|
||||
context "as a site admin" do
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
user.spree_roles << Spree::Role.find_or_create_by_name!("admin")
|
||||
user
|
||||
end
|
||||
subject { PackingReport.new user }
|
||||
|
||||
describe "fetching orders" do
|
||||
it "fetches completed orders" do
|
||||
o1 = create(:order)
|
||||
o2 = create(:order, completed_at: 1.day.ago)
|
||||
subject.orders.should == [o2]
|
||||
end
|
||||
|
||||
it "does not show cancelled orders" do
|
||||
o1 = create(:order, state: "canceled", completed_at: 1.day.ago)
|
||||
o2 = create(:order, completed_at: 1.day.ago)
|
||||
subject.orders.should == [o2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise user" do
|
||||
let!(:user) { create_enterprise_user }
|
||||
|
||||
subject { PackingReport.new user }
|
||||
|
||||
describe "fetching orders" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:simple_product, supplier: supplier) }
|
||||
let(:d1) { create(:distributor_enterprise) }
|
||||
let(:oc1) { create(:simple_order_cycle) }
|
||||
let(:order) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) }
|
||||
|
||||
before do
|
||||
d1.enterprise_roles.create!(user: user)
|
||||
end
|
||||
|
||||
it "only shows orders managed by the current user" do
|
||||
d2 = create(:distributor_enterprise)
|
||||
d2.enterprise_roles.create!(user: create(:user))
|
||||
o2 = create(:order, distributor: d2, completed_at: 1.day.ago)
|
||||
|
||||
subject.orders.should == [order]
|
||||
end
|
||||
|
||||
it "only shows the selected order cycle" do
|
||||
oc2 = create(:simple_order_cycle)
|
||||
order2 = create(:order, order_cycle: oc2)
|
||||
subject.stub(:params).and_return(order_cycle_id_in: oc1.id)
|
||||
subject.orders.should == [order]
|
||||
end
|
||||
|
||||
it "only shows product line items that I am supplying" do
|
||||
d2 = create(:distributor_enterprise)
|
||||
create(:enterprise_relationship, parent: supplier, child: d1, permissions_list: [:add_to_order_cycle])
|
||||
d2.enterprise_roles.create!(user: create(:user))
|
||||
|
||||
s2 = create(:supplier_enterprise)
|
||||
p2 = create(:simple_product, supplier: s2)
|
||||
|
||||
li1 = create(:line_item, product: product)
|
||||
li2 = create(:line_item, product: p2)
|
||||
o1 = create(:order, distributor: d1, completed_at: 1.day.ago)
|
||||
o1.line_items << li1
|
||||
o2 = create(:order, distributor: d2, completed_at: 1.day.ago)
|
||||
o2.line_items << li2
|
||||
subject.orders.map{ |o| o.line_items}.flatten.should include li1
|
||||
subject.orders.map{ |o| o.line_items}.flatten.should_not include li2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,6 +5,7 @@ module OpenFoodNetwork
|
||||
let(:hub) { create(:distributor_enterprise) }
|
||||
let(:v) { create(:variant, price: 11.11, count_on_hand: 1) }
|
||||
let(:vo) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: 2) }
|
||||
let(:vo_price_only) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: nil) }
|
||||
let(:scoper) { ScopeVariantToHub.new(hub) }
|
||||
|
||||
describe "overriding price" do
|
||||
@@ -44,6 +45,27 @@ module OpenFoodNetwork
|
||||
scoper.scope v
|
||||
v.count_on_hand.should == 1
|
||||
end
|
||||
|
||||
describe "overriding stock on an on_demand variant" do
|
||||
let(:v) { create(:variant, price: 11.11, on_demand: true) }
|
||||
|
||||
it "clears on_demand when the stock is overridden" do
|
||||
vo
|
||||
scoper.scope v
|
||||
v.on_demand.should be_false
|
||||
end
|
||||
|
||||
it "does not clear on_demand when only the price is overridden" do
|
||||
vo_price_only
|
||||
scoper.scope v
|
||||
v.on_demand.should be_true
|
||||
end
|
||||
|
||||
it "does not clear on_demand when there is no override" do
|
||||
scoper.scope v
|
||||
v.on_demand.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user