Merge branch 'master' of github.com:openfoodfoundation/openfoodnetwork into show-order-without-current-distributor

This commit is contained in:
Maikel Linke
2015-06-03 20:36:12 +10:00
70 changed files with 608 additions and 194 deletions

3
.rspec_parallel Normal file
View File

@@ -0,0 +1,3 @@
--format progress
--format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log
--tag ~performance

View File

@@ -112,4 +112,5 @@ group :development do
gem 'guard-rails'
gem 'guard-zeus'
gem 'guard-rspec'
gem 'parallel_tests'
end

View File

@@ -362,6 +362,9 @@ GEM
activesupport (>= 3.0.0)
cocaine (~> 0.5.3)
mime-types
parallel (1.4.1)
parallel_tests (1.3.7)
parallel
paypal-sdk-core (0.2.10)
multi_json (~> 1.0)
xml-simple
@@ -575,6 +578,7 @@ DEPENDENCIES
newrelic_rpm
oj
paperclip
parallel_tests
pg
poltergeist
pry-debugger

View File

@@ -1,4 +1,4 @@
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $http, BulkProducts, DisplayProperties, dataFetcher, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, SpreeApiAuth) ->
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $http, BulkProducts, DisplayProperties, dataFetcher, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, SpreeApiAuth, tax_categories) ->
$scope.loading = true
$scope.StatusMessage = StatusMessage
@@ -10,7 +10,9 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
unit: {name: "Unit", visible: true}
price: {name: "Price", visible: true}
on_hand: {name: "On Hand", visible: true}
on_demand: {name: "On Demand", visible: false}
category: {name: "Category", visible: false}
tax_category: {name: "Tax Category", visible: false}
inherits_properties: {name: "Inherits Properties?", visible: false}
available_on: {name: "Available On", visible: false}
@@ -32,6 +34,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.producers = producers
$scope.taxons = Taxons.taxons
$scope.tax_categories = tax_categories
$scope.filterProducers = [{id: "0", name: ""}].concat $scope.producers
$scope.filterTaxons = [{id: "0", name: ""}].concat $scope.taxons
$scope.producerFilter = "0"
@@ -106,6 +109,12 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
window.location = "/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit"
$scope.toggleShowAllVariants = ->
showVariants = !DisplayProperties.showVariants 0
$scope.filteredProducts.forEach (product) ->
DisplayProperties.setShowVariants product.id, showVariants
DisplayProperties.setShowVariants 0, showVariants
$scope.addVariant = (product) ->
product.variants.push
id: $scope.nextVariantId()
@@ -312,9 +321,15 @@ filterSubmitProducts = (productsToFilter) ->
if product.hasOwnProperty("on_hand") and filteredVariants.length == 0 #only update if no variants present
filteredProduct.on_hand = product.on_hand
hasUpdatableProperty = true
if product.hasOwnProperty("on_demand") and filteredVariants.length == 0 #only update if no variants present
filteredProduct.on_demand = product.on_demand
hasUpdatableProperty = true
if product.hasOwnProperty("category_id")
filteredProduct.primary_taxon_id = product.category_id
hasUpdatableProperty = true
if product.hasOwnProperty("tax_category_id")
filteredProduct.tax_category_id = product.tax_category_id
hasUpdatableProperty = true
if product.hasOwnProperty("inherits_properties")
filteredProduct.inherits_properties = product.inherits_properties
hasUpdatableProperty = true
@@ -340,6 +355,9 @@ filterSubmitVariant = (variant) ->
if variant.hasOwnProperty("on_hand")
filteredVariant.on_hand = variant.on_hand
hasUpdatableProperty = true
if variant.hasOwnProperty("on_demand")
filteredVariant.on_demand = variant.on_demand
hasUpdatableProperty = true
if variant.hasOwnProperty("price")
filteredVariant.price = variant.price
hasUpdatableProperty = true

View File

@@ -1,10 +1,8 @@
angular.module("ofn.admin").directive "ofnToggleVariants", (DisplayProperties) ->
link: (scope, element, attrs) ->
if DisplayProperties.showVariants scope.product.id
element.removeClass "icon-chevron-right"
element.addClass "icon-chevron-down"
else
element.removeClass "icon-chevron-down"
element.addClass "icon-chevron-right"
element.on "click", ->
@@ -16,4 +14,4 @@ angular.module("ofn.admin").directive "ofnToggleVariants", (DisplayProperties) -
else
DisplayProperties.setShowVariants scope.product.id, true
element.removeClass "icon-chevron-right"
element.addClass "icon-chevron-down"
element.addClass "icon-chevron-down"

View File

@@ -19,7 +19,7 @@ angular.module("ofn.admin").factory "BulkProducts", (PagedFetcher, dataFetcher)
# when a respond_overrride for the clone action is used.
id = data.product.id
dataFetcher("/api/products/" + id + "?template=bulk_show").then (newProduct) =>
@addProducts [newProduct]
@insertProductAfter(product, newProduct)
updateVariantLists: (serverProducts, productsWithUnsavedVariants) ->
for product in productsWithUnsavedVariants
@@ -39,6 +39,10 @@ angular.module("ofn.admin").factory "BulkProducts", (PagedFetcher, dataFetcher)
@unpackProduct product
@products.push product
insertProductAfter: (product, newProduct) ->
index = @products.indexOf(product)
@products.splice(index + 1, 0, newProduct)
unpackProduct: (product) ->
#$scope.matchProducer product
@loadVariantUnit product

View File

@@ -3,12 +3,10 @@ angular.module("ofn.admin").factory "DisplayProperties", ->
displayProperties: {}
showVariants: (product_id) ->
@initProduct product_id
@displayProperties[product_id].showVariants
@productProperties(product_id).showVariants
setShowVariants: (product_id, showVariants) ->
@initProduct product_id
@displayProperties[product_id].showVariants = showVariants
@productProperties(product_id).showVariants = showVariants
initProduct: (product_id) ->
productProperties: (product_id) ->
@displayProperties[product_id] ||= {showVariants: false}

View File

@@ -32,8 +32,9 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro
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
if product.master
product.master.product = product
product.master = Variants.register product.master
registerVariantsWithCart: ->
for product in @products

View File

@@ -12,9 +12,6 @@ class BaseController < ApplicationController
before_filter :check_order_cycle_expiry
def load_active_distributors
@active_distributors ||= Enterprise.distributors_with_active_order_cycles
end
private

View File

@@ -12,9 +12,6 @@ class CheckoutController < Spree::CheckoutController
include EnterprisesHelper
def edit
# Because this controller doesn't inherit from our BaseController
# We need to duplicate the code here
@active_distributors ||= Enterprise.distributors_with_active_order_cycles
end
def update

View File

@@ -4,7 +4,7 @@ class EnterprisesController < BaseController
include OrderCyclesHelper
# These prepended filters are in the reverse order of execution
prepend_before_filter :load_active_distributors, :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop
prepend_before_filter :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop
before_filter :clean_permalink, only: :check_permalink
respond_to :js, only: :permalink_checker

View File

@@ -1,6 +1,5 @@
class GroupsController < BaseController
layout 'darkswarm'
before_filter :load_active_distributors
def index
@groups = EnterpriseGroup.on_front_page.by_position

View File

@@ -1,11 +1,9 @@
class HomeController < BaseController
layout 'darkswarm'
before_filter :load_active_distributors
def index
end
def about_us
end
end

View File

@@ -1,6 +1,5 @@
class MapController < BaseController
layout 'darkswarm'
before_filter :load_active_distributors
def index
end

View File

@@ -1,7 +1,6 @@
class ProducersController < BaseController
layout 'darkswarm'
before_filter :load_active_distributors
def index
end
end

View File

@@ -10,12 +10,16 @@ class ShopController < BaseController
end
def products
# Can we make this query less slow?
#
if @products = products_for_shop
render status: 200,
json: ActiveModel::ArraySerializer.new(@products, each_serializer: Api::ProductSerializer,
current_order_cycle: current_order_cycle, current_distributor: current_distributor).to_json
json: ActiveModel::ArraySerializer.new(@products,
each_serializer: Api::ProductSerializer,
current_order_cycle: current_order_cycle,
current_distributor: current_distributor,
variants: variants_for_shop_by_id,
master_variants: master_variants_for_shop_by_id).to_json
else
render json: "", status: 404
end
@@ -56,4 +60,30 @@ class ShopController < BaseController
"name ASC"
end
end
def all_variants_for_shop
# We use the in_stock? method here instead of the in_stock scope because we need to
# look up the stock as overridden by VariantOverrides, and the scope method is not affected
# by them.
Spree::Variant.
for_distribution(current_order_cycle, current_distributor).
each { |v| v.scope_to_hub current_distributor }.
select(&:in_stock?)
end
def variants_for_shop_by_id
index_by_product_id all_variants_for_shop.reject(&:is_master)
end
def master_variants_for_shop_by_id
index_by_product_id all_variants_for_shop.select(&:is_master)
end
def index_by_product_id(variants)
variants.inject({}) do |vs, v|
vs[v.product_id] ||= []
vs[v.product_id] << v
vs
end
end
end

View File

@@ -23,6 +23,13 @@ Spree::Admin::OrdersController.class_eval do
distributed_by_user(spree_current_user).
page(params[:page]).
per(params[:per_page] || Spree::Config[:orders_per_page])
# Filter orders by distributor
if params[:distributor_ids]
@orders = @orders.where(distributor_id: params[:distributor_ids])
end
if params[:order_cycle_ids]
@orders = @orders.where(order_cycle_id: params[:order_cycle_ids])
end
} } }
# Overwrite to use confirm_email_for_customer instead of confirm_email.

View File

@@ -50,6 +50,10 @@ module Admin
admin_inject_json_ams_array "ofn.admin", "products", @products, Api::Admin::ProductSerializer
end
def admin_inject_tax_categories
admin_inject_json_ams_array "ofn.admin", "tax_categories", @tax_categories, Api::Admin::TaxCategorySerializer
end
def admin_inject_taxons
admin_inject_json_ams_array "admin.taxons", "taxons", @taxons, Api::Admin::TaxonSerializer
end

View File

@@ -1,6 +1,16 @@
require 'open_food_network/enterprise_injection_data'
module InjectionHelper
def inject_enterprises
inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, active_distributors: @active_distributors
inject_json_ams "enterprises", Enterprise.activated.includes(:address).all, Api::EnterpriseSerializer, enterprise_injection_data
end
def inject_group_enterprises
inject_json_ams "group_enterprises", @group.enterprises, Api::EnterpriseSerializer, enterprise_injection_data
end
def inject_current_hub
inject_json_ams "currentHub", current_distributor, Api::EnterpriseSerializer, enterprise_injection_data
end
def inject_current_order
@@ -53,4 +63,13 @@ module InjectionHelper
end
render partial: "json/injection_ams", locals: {name: name, json: json}
end
private
def enterprise_injection_data
@enterprise_injection_data ||= OpenFoodNetwork::EnterpriseInjectionData.new
{data: @enterprise_injection_data}
end
end

View File

@@ -178,6 +178,10 @@ class Enterprise < ActiveRecord::Base
count(distinct: true)
end
def activated?
confirmed_at.present? && sells != 'unspecified'
end
def set_producer_property(property_name, property_value)
transaction do
property = Spree::Property.where(name: property_name).first_or_create!(presentation: property_name)

View File

@@ -25,6 +25,32 @@ class EnterpriseRelationship < ActiveRecord::Base
scope :by_name, with_enterprises.order('child_enterprises.name, parent_enterprises.name')
# Load an array of the relatives of each enterprise (ie. any enterprise related to it in
# either direction). This array is split into distributors and producers, and has the format:
# {enterprise_id => {distributors: [id, ...], producers: [id, ...]} }
def self.relatives(activated_only=false)
relationships = EnterpriseRelationship.includes(:child, :parent)
relatives = {}
relationships.each do |r|
relatives[r.parent_id] ||= {distributors: Set.new, producers: Set.new}
relatives[r.child_id] ||= {distributors: Set.new, producers: Set.new}
if !activated_only || r.child.activated?
relatives[r.parent_id][:producers] << r.child_id if r.child.is_primary_producer
relatives[r.parent_id][:distributors] << r.child_id if r.child.is_distributor
end
if !activated_only || r.parent.activated?
relatives[r.child_id][:producers] << r.parent_id if r.parent.is_primary_producer
relatives[r.child_id][:distributors] << r.parent_id if r.parent.is_distributor
end
end
relatives
end
def permissions_list=(perms)
perms.andand.each { |name| permissions.build name: name }
end

View File

@@ -92,11 +92,25 @@ class OrderCycle < ActiveRecord::Base
with_distributor(distributor).soonest_closing.first
end
def self.most_recently_closed_for(distributor)
with_distributor(distributor).most_recently_closed.first
end
# Find the earliest closing times for each distributor in an active order cycle, and return
# them in the format {distributor_id => closing_time, ...}
def self.earliest_closing_times
Hash[
Exchange.
outgoing.
joins(:order_cycle).
merge(OrderCycle.active).
group('exchanges.receiver_id').
select('exchanges.receiver_id AS receiver_id, MIN(order_cycles.orders_close_at) AS earliest_close_at').
map { |ex| [ex.receiver_id, ex.earliest_close_at.to_time] }
]
end
def clone!
oc = self.dup
oc.name = "COPY OF #{oc.name}"

View File

@@ -109,6 +109,12 @@ Spree::Product.class_eval do
# -- Methods
# Called by Spree::Product::duplicate before saving.
def duplicate_extra(parent)
# Spree sets the SKU to "COPY OF #{parent sku}".
self.master.sku = ''
end
def properties_including_inherited
# Product properties override producer properties
ps = product_properties.all

View File

@@ -25,6 +25,22 @@ Spree::ShippingMethod.class_eval do
scope :by_name, order('spree_shipping_methods.name ASC')
# Return the services (pickup, delivery) that different distributors provide, in the format:
# {distributor_id => {pickup: true, delivery: false}, ...}
def self.services
Hash[
Spree::ShippingMethod.
joins(:distributor_shipping_methods).
group('distributor_id').
select("distributor_id").
select("BOOL_OR(spree_shipping_methods.require_ship_address = 'f') AS pickup").
select("BOOL_OR(spree_shipping_methods.require_ship_address = 't') AS delivery").
map { |sm| [sm.distributor_id.to_i, {pickup: sm.pickup == 't', delivery: sm.delivery == 't'}] }
]
end
def available_to_order_with_distributor_check?(order, display_on=nil)
available_to_order_without_distributor_check?(order, display_on) &&
self.distributors.include?(order.distributor)

View File

@@ -9,4 +9,40 @@ Spree::Taxon.class_eval do
#fs << Spree::ProductFilters.distributor_filter if Spree::ProductFilters.respond_to? :distributor_filter
fs
end
# Find all the taxons of supplied products for each enterprise, indexed by enterprise.
# Format: {enterprise_id => [taxon_id, ...]}
def self.supplied_taxons
taxons = {}
Spree::Taxon.
joins(:products => :supplier).
select('spree_taxons.*, enterprises.id AS enterprise_id').
each do |t|
taxons[t.enterprise_id.to_i] ||= Set.new
taxons[t.enterprise_id.to_i] << t.id
end
taxons
end
# Find all the taxons of distributed products for each enterprise, indexed by enterprise.
# Format: {enterprise_id => [taxon_id, ...]}
def self.distributed_taxons
taxons = {}
Spree::Taxon.
joins(:products).
merge(Spree::Product.with_order_cycles_outer).
where('o_exchanges.incoming = ?', false).
select('spree_taxons.*, o_exchanges.receiver_id AS enterprise_id').
each do |t|
taxons[t.enterprise_id.to_i] ||= Set.new
taxons[t.enterprise_id.to_i] << t.id
end
taxons
end
end

View File

@@ -7,6 +7,7 @@ Spree::Variant.class_eval do
has_many :exchange_variants, dependent: :destroy
has_many :exchanges, through: :exchange_variants
has_many :variant_overrides
attr_accessible :unit_value, :unit_description, :images_attributes, :display_as, :display_name
accepts_nested_attributes_for :images

View File

@@ -0,0 +1,13 @@
/ insert_before "div.clearfix"
.field-block.alpha.eight.columns
= label_tag nil, t(:distributors)
= select_tag(:distributor_ids,
options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]),
{class: "select2 fullwidth", multiple: true})
.field-block.alpha.eight.columns
= label_tag nil, t(:order_cycles)
= select_tag(:order_cycle_ids,
options_for_select(OrderCycle.managed_by(spree_current_user).map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]),
{class: "select2 fullwidth", multiple: true})

View File

@@ -38,20 +38,26 @@
.twelve.columns.alpha
.six.columns.alpha
= render 'spree/admin/products/primary_taxon_form', f: f
.three.columns
.two.columns
= f.field_container :price do
= f.label :price, t(:price)
%span.required *
%br/
= f.text_field :price, class: 'fullwidth'
= f.error_message_on :price
.three.columns.omega
.two.columns
= f.field_container :on_hand do
= f.label :on_hand, t(:on_hand)
%span.required *
%br/
= f.text_field :on_hand, class: 'fullwidth'
= f.error_message_on :on_hand
.two.columns.omega
= f.field_container :on_demand do
= f.label :on_demand, t(:on_demand)
%br/
= f.check_box :on_demand
= f.error_message_on :on_demand
.twelve.columns.alpha
.six.columns.alpha &nbsp;
.three.columns

View File

@@ -1,7 +1,7 @@
class Api::Admin::ProductSerializer < ActiveModel::Serializer
attributes :id, :name, :sku, :variant_unit, :variant_unit_scale, :variant_unit_name, :on_demand, :inherits_properties
attributes :on_hand, :price, :available_on, :permalink_live
attributes :on_hand, :price, :available_on, :permalink_live, :tax_category_id
has_one :supplier, key: :producer_id, embed: :id
has_one :primary_taxon, key: :category_id, embed: :id

View File

@@ -0,0 +1,3 @@
class Api::Admin::TaxCategorySerializer < ActiveModel::Serializer
attributes :id, :name
end

View File

@@ -1,6 +1,7 @@
class Api::Admin::VariantSerializer < ActiveModel::Serializer
attributes :id, :options_text, :unit_value, :unit_description, :unit_to_display, :on_demand, :display_as, :display_name, :name_to_display
attributes :on_hand, :price
has_many :variant_overrides
def on_hand
object.on_hand.nil? ? 0 : ( object.on_hand.to_f.finite? ? object.on_hand : "On demand" )

View File

@@ -1,4 +1,7 @@
class Api::EnterpriseSerializer < ActiveModel::Serializer
# We reference this here because otherwise the serializer complains about its absence
Api::IdSerializer
def serializable_hash
cached_serializer_hash.merge uncached_serializer_hash
end
@@ -6,11 +9,11 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer
private
def cached_serializer_hash
Api::CachedEnterpriseSerializer.new(object, @options).serializable_hash
Api::CachedEnterpriseSerializer.new(object, @options).serializable_hash || {}
end
def uncached_serializer_hash
Api::UncachedEnterpriseSerializer.new(object, @options).serializable_hash
Api::UncachedEnterpriseSerializer.new(object, @options).serializable_hash || {}
end
end
@@ -18,19 +21,22 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer
attributes :orders_close_at, :active
def orders_close_at
OrderCycle.first_closing_for(object).andand.orders_close_at
options[:data].earliest_closing_times[object.id]
end
def active
@options[:active_distributors].andand.include? object
options[:data].active_distributors.andand.include? object
end
end
class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
cached
delegate :cache_key, to: :object
#delegate :cache_key, to: :object
def cache_key
object.andand.cache_key
end
attributes :name, :id, :description, :latitude, :longitude,
:long_description, :website, :instagram, :linkedin, :twitter,
@@ -38,17 +44,27 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
:email, :hash, :logo, :promo_image, :path, :pickup, :delivery,
:icon, :icon_font, :producer_icon_font, :category, :producers, :hubs
has_many :distributed_taxons, key: :taxons, serializer: Api::IdSerializer
has_many :supplied_taxons, serializer: Api::IdSerializer
attributes :taxons, :supplied_taxons
has_one :address, serializer: Api::AddressSerializer
def taxons
ids_to_objs options[:data].distributed_taxons[object.id]
end
def supplied_taxons
ids_to_objs options[:data].supplied_taxons[object.id]
end
def pickup
object.shipping_methods.where(:require_ship_address => false).present?
services = options[:data].shipping_method_services[object.id]
services ? services[:pickup] : false
end
def delivery
object.shipping_methods.where(:require_ship_address => true).present?
services = options[:data].shipping_method_services[object.id]
services ? services[:delivery] : false
end
def email
@@ -72,11 +88,13 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
end
def producers
ActiveModel::ArraySerializer.new(object.suppliers.activated, {each_serializer: Api::IdSerializer})
relatives = options[:data].relatives[object.id]
relatives ? ids_to_objs(relatives[:producers]) : []
end
def hubs
ActiveModel::ArraySerializer.new(object.distributors.activated, {each_serializer: Api::IdSerializer})
relatives = options[:data].relatives[object.id]
relatives ? ids_to_objs(relatives[:distributors]) : []
end
# Map svg icons.
@@ -116,4 +134,11 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
}
icon_fonts[object.category]
end
private
def ids_to_objs(ids)
ids.andand.map { |id| {id: id} }
end
end

View File

@@ -30,8 +30,9 @@ class Api::CachedProductSerializer < ActiveModel::Serializer
#cached
#delegate :cache_key, to: :object
attributes :id, :name, :permalink, :count_on_hand, :on_demand, :group_buy,
:notes, :description, :properties_with_values
attributes :id, :name, :permalink, :count_on_hand
attributes :on_demand, :group_buy, :notes, :description
attributes :properties_with_values
has_many :variants, serializer: Api::VariantSerializer
has_many :taxons, serializer: Api::IdSerializer
@@ -46,13 +47,11 @@ class Api::CachedProductSerializer < ActiveModel::Serializer
end
def variants
# We use the in_stock? method here instead of the in_stock scope because we need to
# look up the stock as overridden by VariantOverrides, and the scope method is not affected
# by them.
object.variants.
for_distribution(options[:current_order_cycle], options[:current_distributor]).
each { |v| v.scope_to_hub options[:current_distributor] }.
select(&:in_stock?)
options[:variants][object.id] || []
end
def master
options[:master_variants][object.id].andand.first
end
end

View File

@@ -3,7 +3,7 @@
%shop.darkswarm
- content_for :order_cycle_form do
%div{"ng-controller" => "OrderCycleChangeCtrl"}
%div{"ng-controller" => "OrderCycleChangeCtrl", "ng-cloak" => true}
%closing{"ng-if" => "OrderCycle.selected()"}
Next order closing
%strong {{ OrderCycle.orders_close_at() | date_in_words }}

View File

@@ -3,8 +3,8 @@
= inject_enterprises
-# inject enterprises in this group
-# further hubs and producers of these enterprises can't be resoleved within this small subset
= inject_json_ams "group_enterprises", @group.enterprises, Api::EnterpriseSerializer, active_distributors: @active_distributors
-# further hubs and producers of these enterprises can't be resolved within this small subset
= inject_group_enterprises
#group-page.row.pad-top{"ng-controller" => "GroupPageCtrl"}
.small-12.columns.pad-top
@@ -95,7 +95,7 @@
= render partial: 'home/fat'
= render partial: 'shared/components/enterprise_no_results'
.small-12.medium-12.large-3.columns
= render partial: 'contact'

View File

@@ -1,6 +0,0 @@
object current_distributor
extends 'json/partials/enterprise'
child suppliers: :producers do
attributes :id
end

View File

@@ -24,7 +24,7 @@
= render partial: "shared/ie_warning"
= javascript_include_tag "iehack"
= inject_json "currentHub", "current_hub"
= inject_current_hub
= inject_json "user", "current_user"
= inject_json "railsFlash", "flash"
= inject_taxons

View File

@@ -2,7 +2,7 @@
%section.left
%a.left-off-canvas-toggle.menu-icon
%span
%section.right
%section.right{"ng-cloak" => true}
.cart
= render partial: "shared/menu/cart"
%a{href: main_app.shop_path}
@@ -11,34 +11,34 @@
%aside.left-off-canvas-menu.show-for-medium-down
%ul.off-canvas-list
%li.ofn-logo
%a{href: root_path}
%a{href: root_path}
%img{src: "/assets/open-food-network-beta.png", srcset: "/assets/open-food-network-beta.svg", width: "110", height: "26"}
- if current_page? root_path
%li.li-menu
%a{"ofn-scroll-to" => "hubs"}
%span.nav-primary
%span.nav-primary
%i.ofn-i_040-hub
Hubs
- else
%li.li-menu
%a{href: root_path + "#/#hubs"}
%span.nav-primary
%span.nav-primary
%i.ofn-i_040-hub
Hubs
%li.li-menu
%a{href: main_app.map_path}
%span.nav-primary
%span.nav-primary
%i.ofn-i_037-map
Map
%li.li-menu
%a{href: main_app.producers_path}
%span.nav-primary
%span.nav-primary
%i.ofn-i_036-producers
Producers
%li.li-menu
%a{href: main_app.groups_path}
%span.nav-primary
%span.nav-primary
%i.ofn-i_035-groups
Groups

View File

@@ -1,4 +1,4 @@
%products.small-12.columns{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null",
%products.small-12.columns{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null", "ng-cloak" => true,
"infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1"}
// TODO: Needs an ng-show to slide content down

View File

@@ -1,11 +1,11 @@
#tabs{"ng-controller" => "TabsCtrl"}
#tabs{"ng-controller" => "TabsCtrl", "ng-cloak" => true}
.row
%tabset
-# Build all tabs.
- for name, heading_cols in { about: ["About #{current_distributor.name}", 6],
producers: ["Producers",2],
- for name, heading_cols in { about: ["About #{current_distributor.name}", 6],
producers: ["Producers",2],
contact: ["Contact",2],
groups: ["Groups",2]}
groups: ["Groups",2]}
-# tabs take tab path in 'active' and 'select' functions defined in TabsCtrl.
- heading, cols = heading_cols
%tab.columns{heading: heading,

View File

@@ -8,3 +8,4 @@
= render 'spree/admin/products/bulk_edit/actions'
= render 'spree/admin/products/bulk_edit/indicators'
= render 'spree/admin/products/bulk_edit/products'
= render 'spree/admin/products/bulk_edit/save_button_row'

View File

@@ -1,3 +1,4 @@
= admin_inject_producers
= admin_inject_taxons
= admin_inject_tax_categories
= admin_inject_spree_api_key

View File

@@ -7,7 +7,9 @@
%col.display_as{ ng: { show: 'columns.unit.visible' } }
%col.price{ ng: { show: 'columns.price.visible'} }
%col.on_hand{ ng: { show: 'columns.on_hand.visible' } }
%col.on_demand{ ng: { show: 'columns.on_demand.visible' } }
%col.category{ ng: { show: 'columns.category.visible' } }
%col.tax_category{ ng: { show: 'columns.tax_category.visible' } }
%col.inherits_properties{ ng: { show: 'columns.inherits_properties.visible' } }
%col.available_on{ ng: { show: 'columns.available_on.visible' } }
%col.actions
@@ -17,6 +19,8 @@
%thead
%tr
%th.left-actions
%a{ 'ng-click' => 'toggleShowAllVariants()', :style => 'color: red' }
Expand All
%th.producer{ 'ng-show' => 'columns.producer.visible' } Producer
%th.sku{ 'ng-show' => 'columns.sku.visible' } SKU
%th.name{ 'ng-show' => 'columns.name.visible' } Name
@@ -24,7 +28,9 @@
%th.display_as{ 'ng-show' => 'columns.unit.visible' } Display As
%th.price{ 'ng-show' => 'columns.price.visible' } Price
%th.on_hand{ 'ng-show' => 'columns.on_hand.visible' } On Hand
%th.on_demand{ 'ng-show' => 'columns.on_demand.visible' } On Demand
%th.category{ 'ng-show' => 'columns.category.visible' } Category
%th.tax_category{ 'ng-show' => 'columns.tax_category.visible' } Tax Category
%th.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' } Inherits Properties?
%th.available_on{ 'ng-show' => 'columns.available_on.visible' } Av. On
%th.actions

View File

@@ -1,6 +1,6 @@
%tr.product{ :id => "p_{{product.id}}" }
%td.left-actions
%a{ 'ofn-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' }
%a{ 'ofn-toggle-variants' => 'true', :class => "view-variants", 'ng-show' => 'hasVariants(product)' }
%a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "!hasVariants(product) && hasUnit(product)" }
%td.producer{ 'ng-show' => 'columns.producer.visible' }
%select.select2.fullwidth{ 'ng-model' => 'product.producer_id', :name => 'producer_id', 'ofn-track-product' => 'producer_id', 'ng-options' => 'producer.id as producer.name for producer in producers' }
@@ -20,8 +20,13 @@
%td.on_hand{ 'ng-show' => 'columns.on_hand.visible' }
%span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-show' => '!hasOnDemandVariants(product) && (hasVariants(product) || product.on_demand)' }
%input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ofn-track-product' => 'on_hand', 'ng-hide' => 'hasVariants(product) || product.on_demand', :type => 'number' }
%td.on_demand{ 'ng-show' => 'columns.on_demand.visible' }
%input.field{ 'ng-model' => 'product.on_demand', :name => 'on_demand', 'ofn-track-product' => 'on_demand', :type => 'checkbox', 'ng-hide' => 'hasVariants(product)' }
%td.category{ 'ng-if' => 'columns.category.visible' }
%input.fullwidth{ :type => 'text', id: "p{{product.id}}_category_id", 'ng-model' => 'product.category_id', 'ofn-taxon-autocomplete' => '', 'ofn-track-product' => 'category_id', 'multiple-selection' => 'false', placeholder: 'Category' }
%td.tax_category{ 'ng-if' => 'columns.tax_category.visible' }
%select.select2{ name: 'product_tax_category_id', 'ofn-track-product' => 'tax_category_id', ng: {model: 'product.tax_category_id', options: 'tax_category.id as tax_category.name for tax_category in tax_categories'} }
%option{value: ''} None
%td.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' }
%input{ 'ng-model' => 'product.inherits_properties', :name => 'inherits_properties', 'ofn-track-product' => 'inherits_properties', type: "checkbox" }
%td.available_on{ 'ng-show' => 'columns.available_on.visible' }

View File

@@ -15,11 +15,15 @@
%td{ 'ng-show' => 'columns.on_hand.visible' }
%input.field{ 'ng-model' => 'variant.on_hand', 'ng-change' => 'updateOnHand(product)', :name => 'variant_on_hand', 'ng-hide' => 'variant.on_demand', 'ofn-track-variant' => 'on_hand', :type => 'number' }
%span{ 'ng-bind' => 'variant.on_hand', :name => 'variant_on_hand', 'ng-show' => 'variant.on_demand' }
%td{ 'ng-show' => 'columns.on_demand.visible' }
%input.field{ 'ng-model' => 'variant.on_demand', :name => 'variant_on_demand', 'ofn-track-variant' => 'on_demand', :type => 'checkbox' }
%td{ 'ng-show' => 'columns.category.visible' }
%td{ 'ng-show' => 'columns.tax_category.visible' }
%td{ 'ng-show' => 'columns.inherits_properties.visible' }
%td{ 'ng-show' => 'columns.available_on.visible' }
%td.actions
%a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text", 'ng-show' => "variantSaved(variant)" }
%td.actions
%span.icon-warning-sign.with-tip{ 'ng-if' => 'variant.variant_overrides', title: "This variant has {{variant.variant_overrides.length}} override(s)" }
%td.actions
%a{ 'ng-click' => 'deleteVariant(product,variant)', "ng-class" => '{disabled: product.variants.length < 2}', :class => "delete-variant icon-trash no-text" }

View File

@@ -0,0 +1,3 @@
%div.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 0', style: "margin-bottom: 10px" }
%div.four.columns.alpha
%input.four.columns.alpha{ :type => 'button', :value => 'Save Changes', 'ng-click' => 'submitProducts()'}

View File

@@ -10,7 +10,7 @@ development:
test:
adapter: postgresql
encoding: unicode
database: open_food_network_test
database: open_food_network_test<%= ENV['TEST_ENV_NUMBER'] %>
pool: 5
host: localhost
username: ofn

View File

@@ -0,0 +1,27 @@
module OpenFoodNetwork
class EnterpriseInjectionData
def active_distributors
@active_distributors ||= Enterprise.distributors_with_active_order_cycles
end
def earliest_closing_times
@earliest_closing_times ||= OrderCycle.earliest_closing_times
end
def shipping_method_services
@shipping_method_services ||= Spree::ShippingMethod.services
end
def relatives
@relatives ||= EnterpriseRelationship.relatives(true)
end
def supplied_taxons
@supplied_taxons ||= Spree::Taxon.supplied_taxons
end
def distributed_taxons
@distributed_taxons ||= Spree::Taxon.distributed_taxons
end
end
end

View File

@@ -47,7 +47,7 @@ module OpenFoodNetwork
end
def variants
filter(child_variants) + filter(master_variants)
filter(child_variants)
end
def child_variants
@@ -57,16 +57,6 @@ module OpenFoodNetwork
.order("spree_products.name")
end
def master_variants
Spree::Variant.where(:is_master => true)
.joins(:product)
.where("(select spree_variants.id from spree_variants as other_spree_variants
WHERE other_spree_variants.product_id = spree_variants.product_id
AND other_spree_variants.is_master = 'f' LIMIT 1) IS NULL")
.merge(visible_products)
.order("spree_products.name")
end
def filter(variants)
# NOTE: Ordering matters.
# filter_to_order_cycle and filter_to_distributor return Arrays not Arel
@@ -107,7 +97,7 @@ module OpenFoodNetwork
def filter_to_order_cycle(variants)
if params[:order_cycle_id].to_i > 0
order_cycle = OrderCycle.find params[:order_cycle_id]
variants.select! { |v| order_cycle.variants.include? v }
variants.select { |v| order_cycle.variants.include? v }
else
variants
end

View File

@@ -6,5 +6,9 @@ source ./script/ci/includes.sh
echo "--- Verifying branch is based on current master"
exit_unless_master_merged
echo "--- Pushing branch"
git push origin $BUILDKITE_COMMIT:master
echo "--- Merging and pushing branch"
git checkout master
git merge origin/master
git merge origin/$BUILDKITE_BRANCH
git push origin master
git checkout origin/$BUILDKITE_BRANCH

View File

@@ -13,7 +13,8 @@ echo "--- Bundling"
bundle install
echo "--- Loading test database"
bundle exec rake db:test:load
bundle exec rake db:drop db:create db:schema:load
bundle exec rake parallel:drop parallel:create parallel:load_schema
echo "--- Running tests"
bundle exec rspec spec
bundle exec rake parallel:spec

View File

@@ -24,9 +24,4 @@ describe BaseController do
response.should redirect_to root_url
flash[:info].should == "The order cycle you've selected has just closed. Please try again!"
end
it "loads active_distributors" do
Enterprise.stub(:distributors_with_active_order_cycles) { 'active distributors' }
controller.load_active_distributors.should == 'active distributors'
end
end

View File

@@ -9,21 +9,9 @@ describe HomeController do
Enterprise.stub(:distributors_with_active_order_cycles) { [distributor] }
end
it "sets active distributors" do
get :index
assigns[:active_distributors].should == [distributor]
end
# Exclusion from actual rendered view handled in features/consumer/home
it "shows JSON for invisible hubs" do
get :index
response.body.should have_content invisible_distributor.name
end
# This is done inside the json/hubs Serializer
it "gets the next order cycle for each hub" do
OrderCycle.should_receive(:first_closing_for).twice
get :index
end
end

View File

@@ -1,13 +0,0 @@
require 'spec_helper'
describe MapController do
it "loads active distributors" do
active_distributors = double(:distributors)
Enterprise.stub(:distributors_with_active_order_cycles) { active_distributors }
get :index
assigns(:active_distributors).should == active_distributors
end
end

View File

@@ -1,15 +0,0 @@
require 'spec_helper'
describe ProducersController do
let!(:distributor) { create(:distributor_enterprise) }
before do
Enterprise.stub(:distributors_with_active_order_cycles) { [distributor] }
Enterprise.stub(:all).and_return([distributor])
end
it "sets active distributors" do
get :index
assigns[:active_distributors].should == [distributor]
end
end

View File

@@ -176,4 +176,18 @@ describe ShopController do
end
end
end
describe "loading variants" do
let(:hub) { create(:distributor_enterprise) }
let(:oc) { create(:simple_order_cycle, distributors: [hub], variants: [v1]) }
let(:p) { create(:simple_product) }
let!(:v1) { create(:variant, product: p, unit_value: 3) }
let!(:v2) { create(:variant, product: p, unit_value: 5) }
it "scopes variants to distribution" do
controller.stub(:current_order_cycle) { oc }
controller.stub(:current_distributor) { hub }
controller.send(:variants_for_shop_by_id).should == {p.id => [v1]}
end
end
end

View File

@@ -205,8 +205,9 @@ feature %q{
expect(page).to have_selector "a.edit-variant", count: 1
# When I remove two, they should be removed
page.all('a.delete-variant').first.click
page.all('a.delete-variant').first.click
page.all('a.delete-variant', visible: true).first.click
expect(page).to have_selector "tr.variant", count: 2
page.all('a.delete-variant', visible: true).first.click
expect(page).to have_selector "tr.variant", count: 1
# When I fill out variant details and hit update
@@ -216,7 +217,7 @@ feature %q{
fill_in "variant_price", with: "4.0"
fill_in "variant_on_hand", with: "10"
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
updated_variant = Spree::Variant.where(deleted_at: nil).last
@@ -266,7 +267,7 @@ feature %q{
fill_in "product_sku", with: "NEW SKU"
end
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
p.reload
@@ -292,7 +293,7 @@ feature %q{
select "Items", from: "variant_unit_with_scale"
fill_in "variant_unit_name", with: "loaf"
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
p.reload
@@ -326,7 +327,7 @@ feature %q{
expect(page).to have_selector "span[name='on_hand']", text: "10"
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
v.reload
@@ -352,7 +353,7 @@ feature %q{
fill_in "variant_price", with: "10.0"
end
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
v.reload
@@ -369,21 +370,21 @@ feature %q{
fill_in "product_name", with: "new name 1"
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
p.reload
expect(p.name).to eq "new name 1"
fill_in "product_name", with: "new name 2"
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
p.reload
expect(p.name).to eq "new name 2"
fill_in "product_name", with: "original name"
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
p.reload
expect(p.name).to eq "original name"
@@ -399,7 +400,7 @@ feature %q{
fill_in "product_name", :with => "new product name"
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
p.reload
expect(p.name).to eq "new product name"
@@ -412,7 +413,7 @@ feature %q{
visit '/admin/products/bulk_edit'
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "No changes to save."
end
end
@@ -431,7 +432,7 @@ feature %q{
expect(page).to have_no_field "product_name", with: p2.name
fill_in "product_name", :with => "new product1"
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
p1.reload
expect(p1.name).to eq "new product1"
@@ -709,7 +710,7 @@ feature %q{
fill_in "variant_display_as", with: "Big Bag"
end
click_button 'Save Changes'
first(:button, 'Save Changes').click
expect(page.find("#status-message")).to have_content "Changes saved."
p.reload

View File

@@ -4,7 +4,7 @@ describe InjectionHelper do
let!(:enterprise) { create(:distributor_enterprise, facebook: "roger") }
it "will inject via AMS" do
helper.inject_json_ams("test", [enterprise], Api::EnterpriseSerializer).should match enterprise.name
helper.inject_json_ams("test", [enterprise], Api::IdSerializer).should match /#{enterprise.id}/
end
it "injects enterprises" do

View File

@@ -61,14 +61,14 @@ describe "BulkProducts service", ->
clonedProduct =
id: 17
spyOn(BulkProducts, "addProducts")
spyOn(BulkProducts, "insertProductAfter")
BulkProducts.products = [originalProduct]
$httpBackend.expectGET("/admin/products/oranges/clone.json").respond 200,
product: clonedProduct
$httpBackend.expectGET("/api/products/17?template=bulk_show").respond 200, clonedProduct
BulkProducts.cloneProduct BulkProducts.products[0]
$httpBackend.flush()
expect(BulkProducts.addProducts).toHaveBeenCalledWith [clonedProduct]
expect(BulkProducts.insertProductAfter).toHaveBeenCalledWith originalProduct, clonedProduct
describe "preparing products", ->

View File

@@ -215,6 +215,7 @@ describe "filtering products for submission to database", ->
variant_unit_scale: 1
variant_unit_name: 'loaf'
available_on: available_on
tax_category_id: null
master_attributes:
id: 2
unit_value: 250
@@ -238,6 +239,7 @@ describe "AdminProductEditCtrl", ->
module ($provide)->
$provide.value "producers", []
$provide.value "taxons", []
$provide.value "tax_categories", []
$provide.value 'SpreeApiKey', 'API_KEY'
null

View File

@@ -0,0 +1,17 @@
require 'spec_helper'
module OpenFoodNetwork
describe EnterpriseInjectionData do
describe "relatives" do
let!(:enterprise) { create(:distributor_enterprise) }
let!(:producer) { create(:supplier_enterprise) }
let!(:producer_inactive) { create(:supplier_enterprise, confirmed_at: nil) }
let!(:er_p) { create(:enterprise_relationship, parent: producer, child: enterprise) }
let!(:er_pi) { create(:enterprise_relationship, parent: producer_inactive, child: enterprise) }
it "only loads activated relatives" do
subject.relatives[enterprise.id][:producers].should_not include producer_inactive.id
end
end
end
end

View File

@@ -54,10 +54,8 @@ module OpenFoodNetwork
it "fetches variants for some params" do
subject.should_receive(:child_variants).and_return ["children"]
subject.should_receive(:master_variants).and_return ["masters"]
subject.should_receive(:filter).with(['children']).and_return ["filter_children"]
subject.should_receive(:filter).with(['masters']).and_return ["filter_masters"]
subject.variants.should == ["filter_children", "filter_masters"]
subject.variants.should == ["filter_children"]
end
end
@@ -92,14 +90,6 @@ module OpenFoodNetwork
end
end
describe "fetching master variants" do
it "doesn't return master variants with siblings" do
product = create(:simple_product, supplier: supplier)
subject.master_variants.should be_empty
end
end
describe "Filtering variants" do
let(:variants) { Spree::Variant.scoped.joins(:product).where(is_master: false) }
it "should return unfiltered variants sans-params" do

View File

@@ -69,4 +69,32 @@ describe EnterpriseRelationship do
EnterpriseRelationship.with_permission('two').should match_array [er1, er2]
end
end
describe "finding relatives" do
let(:e1) { create(:supplier_enterprise) }
let(:e2) { create(:supplier_enterprise, sells: 'any') }
let!(:er) { create(:enterprise_relationship, parent: e1, child: e2) }
let(:er_reverse) { create(:enterprise_relationship, parent: e2, child: e1) }
it "categorises enterprises into distributors and producers" do
EnterpriseRelationship.relatives.should ==
{e1.id => {distributors: Set.new([e2.id]), producers: Set.new([e2.id])},
e2.id => {distributors: Set.new([]), producers: Set.new([e1.id])}}
end
it "finds inactive enterprises by default" do
e1.update_attribute :confirmed_at, nil
EnterpriseRelationship.relatives[e2.id][:producers].should == Set.new([e1.id])
end
it "does not find inactive enterprises when requested" do
e1.update_attribute :confirmed_at, nil
EnterpriseRelationship.relatives(true)[e2.id][:producers].should be_empty
end
it "does not show duplicates" do
er_reverse
EnterpriseRelationship.relatives[e2.id][:producers].should == Set.new([e1.id])
end
end
end

View File

@@ -422,4 +422,23 @@ describe OrderCycle do
OrderCycle.first_closing_for(distributor).should == oc
end
end
describe "finding the earliest closing times for each distributor" do
let(:time1) { 1.week.from_now }
let(:time2) { 2.weeks.from_now }
let(:time3) { 3.weeks.from_now }
let(:e1) { create(:distributor_enterprise) }
let(:e2) { create(:distributor_enterprise) }
let!(:oc1) { create(:simple_order_cycle, orders_close_at: time1, distributors: [e1]) }
let!(:oc2) { create(:simple_order_cycle, orders_close_at: time2, distributors: [e2]) }
let!(:oc3) { create(:simple_order_cycle, orders_close_at: time3, distributors: [e2]) }
it "returns the closing time, indexed by enterprise id" do
OrderCycle.earliest_closing_times[e1.id].should == time1
end
it "returns the earliest closing time" do
OrderCycle.earliest_closing_times[e2.id].should == time2
end
end
end

View File

@@ -55,5 +55,32 @@ module Spree
sm.should be_available_to_order o
end
end
describe "finding services offered by all distributors" do
let!(:d1) { create(:distributor_enterprise) }
let!(:d2) { create(:distributor_enterprise) }
let!(:d3) { create(:distributor_enterprise) }
let!(:d4) { create(:distributor_enterprise) }
let!(:d1_pickup) { create(:shipping_method, require_ship_address: false, distributors: [d1]) }
let!(:d1_delivery) { create(:shipping_method, require_ship_address: true, distributors: [d1]) }
let!(:d2_pickup) { create(:shipping_method, require_ship_address: false, distributors: [d2]) }
let!(:d3_delivery) { create(:shipping_method, require_ship_address: true, distributors: [d3]) }
it "reports when the services are available" do
ShippingMethod.services[d1.id].should == {pickup: true, delivery: true}
end
it "reports when only pickup is available" do
ShippingMethod.services[d2.id].should == {pickup: true, delivery: false}
end
it "reports when only delivery is available" do
ShippingMethod.services[d3.id].should == {pickup: false, delivery: true}
end
it "returns no entry when no service is available" do
ShippingMethod.services[d4.id].should be_nil
end
end
end
end

View File

@@ -0,0 +1,28 @@
require 'spec_helper'
module Spree
describe Taxon do
let(:e) { create(:supplier_enterprise) }
let(:t0) { p1.taxons.order('id ASC').first }
let(:t1) { create(:taxon) }
let(:t2) { create(:taxon) }
describe "finding all supplied taxons" do
let!(:p1) { create(:simple_product, supplier: e, taxons: [t1, t2]) }
it "finds taxons" do
Taxon.supplied_taxons.should == {e.id => Set.new([t0.id, t1.id, t2.id])}
end
end
describe "finding all distributed taxons" do
let!(:oc) { create(:simple_order_cycle, distributors: [e], variants: [p1.master]) }
let(:s) { create(:supplier_enterprise) }
let(:p1) { create(:simple_product, supplier: s, taxons: [t1, t2]) }
it "finds taxons" do
Taxon.distributed_taxons.should == {e.id => Set.new([t0.id, t1.id, t2.id])}
end
end
end
end

View File

@@ -0,0 +1,29 @@
require 'spec_helper'
describe InjectionHelper, type: :helper, performance: true do
let(:oc) { create(:simple_order_cycle) }
let(:relative_supplier) { create(:supplier_enterprise) }
let(:relative_distributor) { create(:distributor_enterprise) }
before do
50.times do
e = create(:enterprise)
oc.distributors << e
create(:enterprise_relationship, parent: e, child: relative_supplier)
create(:enterprise_relationship, parent: e, child: relative_distributor)
end
end
it "is performant in injecting enterprises" do
results = []
4.times do |i|
ActiveRecord::Base.connection.query_cache.clear
Rails.cache.clear
result = Benchmark.measure { helper.inject_enterprises }
results << result.total if i > 0
puts result
end
puts (results.sum / results.count * 1000).round 0
end
end

View File

@@ -0,0 +1,46 @@
require 'spec_helper'
describe ShopController, type: :controller, performance: true do
let(:d) { create(:distributor_enterprise) }
let(:enterprise_fee) { create(:enterprise_fee) }
let(:order_cycle) { create(:simple_order_cycle, distributors: [d], coordinator_fees: [enterprise_fee]) }
before do
controller.stub(:current_distributor) { d }
controller.stub(:current_order_cycle) { order_cycle }
end
describe "fetching products" do
let(:exchange) { order_cycle.exchanges.to_enterprises(d).outgoing.first }
let(:image) { File.open(File.expand_path('../../../app/assets/images/logo.jpg', __FILE__)) }
before do
11.times do
p = create(:simple_product)
p.set_property 'Organic Certified', 'NASAA 12345'
v1 = create(:variant, product: p)
v2 = create(:variant, product: p)
Spree::Image.create! viewable_id: p.master.id, viewable_type: 'Spree::Variant', attachment: image
exchange.variants << [v1, v2]
end
end
it "returns products via json" do
results = []
4.times do |i|
ActiveRecord::Base.connection.query_cache.clear
Rails.cache.clear
result = Benchmark.measure do
xhr :get, :products
response.should be_success
end
results << result.total if i > 0
puts result
end
puts (results.sum / results.count * 1000).round 0
end
end
end

View File

@@ -1,21 +1,31 @@
#require 'spec_helper'
describe Api::EnterpriseSerializer do
let(:serializer) { Api::EnterpriseSerializer.new enterprise, data: data }
let(:enterprise) { create(:distributor_enterprise) }
let(:taxon) { create(:taxon) }
let(:data) { OpenStruct.new(earliest_closing_times: {},
active_distributors: [],
distributed_taxons: {enterprise.id => [123]},
supplied_taxons: {enterprise.id => [456]},
shipping_method_services: {},
relatives: {enterprise.id => {producers: [123], distributors: [456]}}) }
it "serializes an enterprise" do
serializer = Api::EnterpriseSerializer.new enterprise
serializer.to_json.should match enterprise.name
end
it "includes distributed taxons" do
enterprise.stub(:distributed_taxons).and_return [taxon]
serializer = Api::EnterpriseSerializer.new enterprise
serializer.to_json.should match taxon.id.to_s
it "serializes taxons as ids only" do
serializer.serializable_hash[:taxons].should == [{id: 123}]
serializer.serializable_hash[:supplied_taxons].should == [{id: 456}]
end
it "will render urls" do
serializer = Api::EnterpriseSerializer.new enterprise
it "serializes producers and hubs as ids only" do
serializer.serializable_hash[:producers].should == [{id: 123}]
serializer.serializable_hash[:hubs].should == [{id: 456}]
end
it "serializes icons" do
serializer.to_json.should match "map_005-hub.svg"
end
end

View File

@@ -1,14 +0,0 @@
describe Api::ProductSerializer do
let(:hub) { create(:distributor_enterprise) }
let(:oc) { create(:simple_order_cycle, distributors: [hub], variants: [v1]) }
let(:p) { create(:simple_product) }
let!(:v1) { create(:variant, product: p, unit_value: 3) }
let!(:v2) { create(:variant, product: p, unit_value: 5) }
it "scopes variants to distribution" do
s = Api::ProductSerializer.new p, current_distributor: hub, current_order_cycle: oc
json = s.to_json
json.should include v1.options_text
json.should_not include v2.options_text
end
end