Merge master into producer-emails.

This commit is contained in:
Paul Mackay
2015-03-04 19:09:43 +00:00
468 changed files with 11434 additions and 3239 deletions

View File

@@ -0,0 +1,13 @@
module OpenFoodNetwork::Locking
# http://rhnh.net/2010/06/30/acts-as-list-will-break-in-production
def with_isolation_level_serializable
self.transaction do
self.connection.execute "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE"
yield
end
end
end
class ActiveRecord::Base
extend OpenFoodNetwork::Locking
end

View File

@@ -0,0 +1,61 @@
module OpenFoodNetwork
class OrderCycleManagementReport
attr_reader :params
def initialize(user, params = {})
@params = params
@user = user
end
def header
["First Name", "Last Name", "Email", "Phone", "Hub", "Shipping Method", "Payment Method", "Amount"]
end
def table
orders.map do |order|
ba = order.billing_address
da = order.distributor.andand.address
[ba.firstname,
ba.lastname,
order.email,
ba.phone,
order.distributor.andand.name,
order.shipping_method.andand.name,
order.payments.first.andand.payment_method.andand.name,
order.payments.first.amount
]
end
end
def orders
filter Spree::Order.managed_by(@user).distributed_by_user(@user).complete.where("spree_orders.state != ?", :canceled)
end
def filter(orders)
filter_to_order_cycle filter_to_payment_method filter_to_shipping_method orders
end
def filter_to_payment_method(orders)
if params[:payment_method_name].present?
orders.with_payment_method_name(params[:payment_method_name])
else
orders
end
end
def filter_to_shipping_method(orders)
if params[:shipping_method_name].present?
orders.joins(:shipping_method).where("spree_shipping_methods.name = ?", params[:shipping_method_name])
else
orders
end
end
def filter_to_order_cycle(orders)
if params[:order_cycle_id].present?
orders.where(order_cycle_id: params[:order_cycle_id])
else
orders
end
end
end
end

View File

@@ -4,14 +4,58 @@ module OpenFoodNetwork
@user = user
end
def can_manage_complex_order_cycles?
managed_and_related_enterprises_with(:add_to_order_cycle).any? do |e|
e.sells == 'any'
end
end
# Find enterprises that an admin is allowed to add to an order cycle
def order_cycle_enterprises
managed_and_related_enterprises_with :add_to_order_cycle
end
# Find enterprises for which an admin is allowed to edit their profile
def editable_enterprises
managed_and_related_enterprises_with :edit_profile
end
def variant_override_producers
producer_ids = variant_override_enterprises_per_hub.values.flatten.uniq
Enterprise.where(id: producer_ids)
end
# For every hub that an admin manages, show all the producers for which that hub may
# override variants
# {hub1_id => [producer1_id, producer2_id, ...], ...}
def variant_override_enterprises_per_hub
hubs = managed_and_related_enterprises_with(:add_to_order_cycle).is_distributor
# Permissions granted by create_variant_overrides relationship from producer to hub
permissions = Hash[
EnterpriseRelationship.
permitting(hubs).
with_permission(:create_variant_overrides).
group_by { |er| er.child_id }.
map { |child_id, ers| [child_id, ers.map { |er| er.parent_id }] }
]
# We have permission to create variant overrides for any producers we manage, for any
# hub we can add to an order cycle
managed_producer_ids = managed_enterprises.is_primary_producer.pluck(:id)
if managed_producer_ids.any?
hubs.each do |hub|
permissions[hub.id] = ((permissions[hub.id] || []) + managed_producer_ids).uniq
end
end
permissions
end
# Find the exchanges of an order cycle that an admin can manage
def order_cycle_exchanges(order_cycle)
enterprises = managed_enterprises + related_enterprises_with(:add_to_order_cycle)
enterprises = managed_and_related_enterprises_with :add_to_order_cycle
order_cycle.exchanges.to_enterprises(enterprises).from_enterprises(enterprises)
end
@@ -25,9 +69,20 @@ module OpenFoodNetwork
managed_and_related_enterprises_with :manage_products
end
def manages_one_enterprise?
@user.enterprises.length == 1
end
private
def managed_and_related_enterprises_with(permission)
managed_enterprise_ids = managed_enterprises.pluck :id
permitting_enterprise_ids = related_enterprises_with(permission).pluck :id
Enterprise.where('id IN (?)', managed_enterprise_ids + permitting_enterprise_ids)
end
def managed_enterprises
Enterprise.managed_by(@user)
end
@@ -41,14 +96,6 @@ module OpenFoodNetwork
Enterprise.where('id IN (?)', parent_ids)
end
def managed_and_related_enterprises_with(permission)
managed_enterprise_ids = managed_enterprises.pluck :id
permitted_enterprise_ids = related_enterprises_with(permission).pluck :id
Enterprise.where('id IN (?)', managed_enterprise_ids + permitted_enterprise_ids)
end
def managed_enterprise_products
Spree::Product.managed_by(@user)
end

View File

@@ -13,6 +13,7 @@ module OpenFoodNetwork
"Producer Suburb",
"Product",
"Product Properties",
"Taxons",
"Variant Value",
"Price",
"Group Buy Unit Quantity",
@@ -26,6 +27,7 @@ module OpenFoodNetwork
variant.product.supplier.address.city,
variant.product.name,
variant.product.properties.map(&:name).join(", "),
variant.product.taxons.map(&:name).join(", "),
variant.full_name,
variant.price,
variant.product.group_buy_unit_size,
@@ -58,7 +60,11 @@ module OpenFoodNetwork
def filter(variants)
# NOTE: Ordering matters.
# filter_to_order_cycle and filter_to_distributor return Arrays not Arel
filter_to_distributor filter_to_order_cycle filter_on_hand filter_to_supplier variants
filter_to_distributor filter_to_order_cycle filter_on_hand filter_to_supplier filter_not_deleted variants
end
def filter_not_deleted(variants)
variants.not_deleted
end
def filter_on_hand(variants)

View File

@@ -0,0 +1,16 @@
require 'open_food_network/scope_variant_to_hub'
module OpenFoodNetwork
module ScopeProductToHub
def variants_distributed_by(order_cycle, distributor)
super.each { |v| v.scope_to_hub @hub }
end
end
module ProductScopableToHub
def scope_to_hub(hub)
extend OpenFoodNetwork::ScopeProductToHub
@hub = hub
end
end
end

View File

@@ -0,0 +1,30 @@
module OpenFoodNetwork
module ScopeVariantToHub
def price
VariantOverride.price_for(@hub, self) || super
end
def price_in(currency)
Spree::Price.new(amount: price, currency: currency)
end
def count_on_hand
VariantOverride.count_on_hand_for(@hub, self) || super
end
def decrement!(attribute, by=1)
if attribute == :count_on_hand && VariantOverride.stock_overridden?(@hub, self)
VariantOverride.decrement_stock! @hub, self, by
else
super
end
end
end
module VariantScopableToHub
def scope_to_hub(hub)
extend OpenFoodNetwork::ScopeVariantToHub
@hub = hub
end
end
end

View File

@@ -0,0 +1,32 @@
module OpenFoodNetwork
class UserBalanceCalculator
def initialize(user, distributor)
@user = user
@distributor = distributor
end
def balance
payment_total - order_total
end
private
def order_total
orders.sum &:total
end
def payment_total
payments.sum &:amount
end
def orders
Spree::Order.where(distributor_id: @distributor, user_id: @user)
end
def payments
Spree::Payment.where(order_id: orders)
end
end
end

View File

@@ -0,0 +1,88 @@
module OpenFoodNetwork
class UsersAndEnterprisesReport
attr_reader :params
def initialize(params = {})
@params = params
# Convert arrays of ids to comma delimited strings
@params[:enterprise_id_in] = @params[:enterprise_id_in].join(',') if @params[:enterprise_id_in].kind_of? Array
@params[:user_id_in] = @params[:user_id_in].join(',') if @params[:user_id_in].kind_of? Array
end
def header
[
"User",
"Relationship",
"Enterprise",
"Producer?",
"Sells",
"Visible",
"Confirmation Date"
]
end
def table
users_and_enterprises.map do |uae| [
uae["user_email"],
uae["relationship_type"],
uae["name"],
to_bool(uae["is_primary_producer"]),
uae["sells"],
uae["visible"],
to_local_datetime(uae["confirmed_at"])
]
end
end
def owners_and_enterprises
query = "SELECT enterprises.name, enterprises.sells, enterprises.visible, enterprises.is_primary_producer, enterprises.confirmed_at,
'owns' AS relationship_type, owners.email as user_email FROM enterprises
LEFT JOIN spree_users AS owners ON owners.id=enterprises.owner_id
WHERE enterprises.id IS NOT NULL
#{ params[:enterprise_id_in].present? ? "AND enterprises.id IN (#{ params[:enterprise_id_in] })" : "" }
#{ params[:user_id_in].present? ? "AND owners.id IN (#{ params[:user_id_in] })" : "" }
ORDER BY confirmed_at DESC"
ActiveRecord::Base.connection.execute(query).to_a
end
def managers_and_enterprises
query = "SELECT enterprises.name, enterprises.sells, enterprises.visible, enterprises.is_primary_producer, enterprises.confirmed_at,
'manages' AS relationship_type, managers.email as user_email FROM enterprises
LEFT JOIN enterprise_roles ON enterprises.id=enterprise_roles.enterprise_id
LEFT JOIN spree_users AS managers ON enterprise_roles.user_id=managers.id
WHERE enterprise_id IS NOT NULL
#{ params[:enterprise_id_in].present? ? "AND enterprise_id IN (#{ params[:enterprise_id_in] })" : "" }
AND user_id IS NOT NULL
#{ params[:user_id_in].present? ? "AND user_id IN (#{ params[:user_id_in] })" : "" }
ORDER BY confirmed_at DESC"
ActiveRecord::Base.connection.execute(query).to_a
end
def users_and_enterprises
sort( owners_and_enterprises.concat managers_and_enterprises )
end
def sort(results)
results.sort do |a,b|
if a["confirmed_at"].nil? || b["confirmed_at"].nil?
[ (a["confirmed_at"].nil? ? 0 : 1), a["name"], b["relationship_type"], a["user_email"] ] <=>
[ (b["confirmed_at"].nil? ? 0 : 1), b["name"], a["relationship_type"], b["user_email"] ]
else
[ DateTime.parse(b["confirmed_at"]), a["name"], b["relationship_type"], a["user_email"] ] <=>
[ DateTime.parse(a["confirmed_at"]), b["name"], a["relationship_type"], b["user_email"] ]
end
end
end
def to_bool(value)
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
def to_local_datetime(string)
return "Not Confirmed" if string.nil?
string.to_datetime.in_time_zone.strftime "%Y-%m-%d %H:%M"
end
end
end

View File

@@ -7,6 +7,7 @@ module Spree
user = stub_model(Spree::LegacyUser)
user.stub(:has_spree_role?).with("admin").and_return(false)
user.stub(:enterprises) { [] }
user.stub(:owned_groups) { [] }
user
end
end
@@ -33,6 +34,7 @@ module Spree
# Stub enterprises, needed for cancan ability checks
user.stub(:enterprises) { [] }
user.stub(:owned_groups) { [] }
user
end

View File

@@ -1,4 +1,18 @@
Spree::Core::ControllerHelpers::Order.class_eval do
def current_order_with_scoped_variants(create_order_if_necessary = false)
order = current_order_without_scoped_variants(create_order_if_necessary)
if order
order.line_items.each do |li|
li.variant.scope_to_hub order.distributor
end
end
order
end
alias_method_chain :current_order, :scoped_variants
# Override definition in spree/auth/app/controllers/spree/base_controller_decorator.rb
# Do not attempt to merge incomplete and current orders. Instead, destroy the incomplete orders.
def set_current_order

View File

@@ -0,0 +1,7 @@
Spree::Money.class_eval do
# return the currency symbol (on its own) for the current default currency
def self.currency_symbol
Money.new(0, Spree::Config[:currency]).symbol
end
end