mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-07 22:46:06 +00:00
Merge master into producer-emails.
This commit is contained in:
13
lib/open_food_network/locking.rb
Normal file
13
lib/open_food_network/locking.rb
Normal 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
|
||||
61
lib/open_food_network/order_cycle_management_report.rb
Normal file
61
lib/open_food_network/order_cycle_management_report.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
16
lib/open_food_network/scope_product_to_hub.rb
Normal file
16
lib/open_food_network/scope_product_to_hub.rb
Normal 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
|
||||
30
lib/open_food_network/scope_variant_to_hub.rb
Normal file
30
lib/open_food_network/scope_variant_to_hub.rb
Normal 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
|
||||
32
lib/open_food_network/user_balance_calculator.rb
Normal file
32
lib/open_food_network/user_balance_calculator.rb
Normal 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
|
||||
88
lib/open_food_network/users_and_enterprises_report.rb
Normal file
88
lib/open_food_network/users_and_enterprises_report.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
7
lib/spree/money_decorator.rb
Normal file
7
lib/spree/money_decorator.rb
Normal 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
|
||||
Reference in New Issue
Block a user