diff --git a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee
index 2721c92961..769cec1c18 100644
--- a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee
+++ b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee
@@ -1,5 +1,6 @@
-angular.module("admin.customers").controller "customersCtrl", ($scope, $q, $filter, Customers, TagRuleResource, CurrentShop, RequestMonitor, Columns, pendingChanges, shops) ->
+angular.module("admin.customers").controller "customersCtrl", ($scope, $q, $filter, Customers, TagRuleResource, CurrentShop, RequestMonitor, Columns, pendingChanges, shops, availableCountries) ->
$scope.shops = shops
+ $scope.availableCountries = availableCountries
$scope.RequestMonitor = RequestMonitor
$scope.submitAll = pendingChanges.submitAll
$scope.add = Customers.add
diff --git a/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee b/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee
new file mode 100644
index 0000000000..5fb55eaeba
--- /dev/null
+++ b/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee
@@ -0,0 +1,36 @@
+angular.module("admin.customers").directive 'editAddressDialog', ($compile, $templateCache, $filter, DialogDefaults, Customers, StatusMessage) ->
+ restrict: 'A'
+ scope: true
+ link: (scope, element, attr) ->
+ scope.errors = []
+
+ scope.$watch 'address.country_id', (newVal) ->
+ if newVal
+ scope.states = scope.filter_states(newVal)
+
+ scope.updateAddress = ->
+ scope.edit_address_form.$setPristine()
+ if scope.edit_address_form.$valid
+ Customers.update(scope.address, scope.customer, scope.addressType).$promise.then (data) ->
+ scope.customer = data
+ template.dialog('close')
+ StatusMessage.display('success', t('admin.customers.index.update_address_success'))
+ else
+ scope.errors.push(t('admin.customers.index.update_address_error'))
+
+
+ template = $compile($templateCache.get('admin/edit_address_dialog.html'))(scope)
+ template.dialog(DialogDefaults)
+
+ element.bind 'click', (e) ->
+ if e.target.id == 'bill-address-link'
+ scope.addressType = 'bill_address'
+ else
+ scope.addressType = 'ship_address'
+ scope.address = scope.customer[scope.addressType]
+
+ template.dialog('open')
+ scope.$apply()
+
+ scope.filter_states = (countryID) ->
+ $filter('filter')(scope.availableCountries, {id: countryID})[0].states
diff --git a/app/assets/javascripts/admin/customers/directives/new_customer_dialog.js.coffee b/app/assets/javascripts/admin/customers/directives/new_customer_dialog.js.coffee
index f2dae5836b..60a19838d6 100644
--- a/app/assets/javascripts/admin/customers/directives/new_customer_dialog.js.coffee
+++ b/app/assets/javascripts/admin/customers/directives/new_customer_dialog.js.coffee
@@ -1,4 +1,4 @@
-angular.module("admin.customers").directive 'newCustomerDialog', ($compile, $injector, $templateCache, DialogDefaults, CurrentShop, Customers) ->
+angular.module("admin.customers").directive 'newCustomerDialog', ($compile, $templateCache, DialogDefaults, CurrentShop, Customers) ->
restrict: 'A'
scope: true
link: (scope, element, attr) ->
diff --git a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee b/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee
index 5b6c1ab205..3904d0333d 100644
--- a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee
+++ b/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee
@@ -14,4 +14,8 @@ angular.module("admin.customers").factory 'CustomerResource', ($resource) ->
method: 'DELETE'
params:
id: '@id'
+ 'update':
+ method: 'PUT'
+ params:
+ id: '@id'
})
diff --git a/app/assets/javascripts/admin/customers/services/customers.js.coffee b/app/assets/javascripts/admin/customers/services/customers.js.coffee
index cd4c9bbb44..f4c6f6f3cd 100644
--- a/app/assets/javascripts/admin/customers/services/customers.js.coffee
+++ b/app/assets/javascripts/admin/customers/services/customers.js.coffee
@@ -25,3 +25,11 @@ angular.module("admin.customers").factory "Customers", ($q, InfoDialog, RequestM
request = CustomerResource.index(params, (data) => @customers = data)
RequestMonitor.load(request.$promise)
request.$promise
+
+ update: (address, customer, addressType) ->
+ params =
+ id: customer.id
+ customer:
+ "#{addressType}_attributes": address
+ CustomerResource.update params
+
diff --git a/app/assets/javascripts/admin/index_utils/services/switch_class.js.coffee b/app/assets/javascripts/admin/index_utils/services/switch_class.js.coffee
index c2a3419e2c..0be1f2ec40 100644
--- a/app/assets/javascripts/admin/index_utils/services/switch_class.js.coffee
+++ b/app/assets/javascripts/admin/index_utils/services/switch_class.js.coffee
@@ -1,5 +1,5 @@
angular.module("admin.indexUtils").factory "switchClass", ($timeout) ->
- return (element,classToAdd,removeClasses,timeout) ->
+ return (element, classToAdd, removeClasses, timeout) ->
$timeout.cancel element.timeout if element.timeout
element.removeClass className for className in removeClasses
element.addClass classToAdd
diff --git a/app/assets/javascripts/darkswarm/filters/properties.js.coffee b/app/assets/javascripts/darkswarm/filters/properties.js.coffee
index 51b02e6634..1453cac053 100644
--- a/app/assets/javascripts/darkswarm/filters/properties.js.coffee
+++ b/app/assets/javascripts/darkswarm/filters/properties.js.coffee
@@ -1,5 +1,5 @@
-Darkswarm.filter 'properties', ()->
- # Filter anything that responds to object.properties
+Darkswarm.filter 'properties', ->
+ # Filter anything that responds to object.supplied_properties
(objects, ids) ->
objects ||= []
ids ?= []
@@ -7,10 +7,7 @@ Darkswarm.filter 'properties', ()->
# No properties selected, pass all objects through.
objects
else
- objects.filter (obj)->
- properties = obj.properties
- # Combine object properties with supplied properties, if they exist.
- # properties = properties.concat obj.supplied_properties if obj.supplied_properties
- # Match property array.
- properties.some (property)->
+ objects.filter (obj) ->
+ properties = obj.supplied_properties || obj.properties
+ properties.some (property) ->
property.id in ids
diff --git a/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee b/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee
index 4ab8c94bfd..7eac12bf1e 100644
--- a/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee
+++ b/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee
@@ -1,7 +1,12 @@
Darkswarm.filter 'propertiesOf', ->
- (objects)->
+ (objects) ->
properties = {}
for object in objects
- for property in object.properties
- properties[property.id] = property
+ if object.supplied_properties?
+ for property in object.supplied_properties
+ properties[property.id] = property
+ else
+ for property in object.properties
+ properties[property.id] = property
+
properties
diff --git a/app/assets/javascripts/templates/admin/edit_address_dialog.html.haml b/app/assets/javascripts/templates/admin/edit_address_dialog.html.haml
new file mode 100644
index 0000000000..351a267bd3
--- /dev/null
+++ b/app/assets/javascripts/templates/admin/edit_address_dialog.html.haml
@@ -0,0 +1,61 @@
+#edit-address-dialog
+ %h2 {{ addressType === 'bill_address' ? "#{t('admin.customers.index.edit_bill_address')}" : "#{t('admin.customers.index.edit_ship_address')}" }}
+ %form{ name: 'edit_address_form', novalidate: true, ng: { submit: 'updateAddress()'}}
+ .row
+ = t('admin.customers.index.required_fileds')
+ (
+ %span.required *
+ )
+ .error{ ng: { repeat: "error in errors", bind: "error" } }
+
+ %table.no-borders
+ %tr
+ %td{style: 'width: 30%'}
+ = t('spree.street_address')
+ %span.required *
+ %td
+ %input{ type: 'text', name: 'address1', required: true, ng: { model: 'address.address1'} }
+ %tr
+ %td
+ = t('spree.street_address_1')
+ %td
+ %input{ type: 'text', name: 'address2', ng: { model: 'address.address2'} }
+ %tr
+ %td
+ = t('spree.phone')
+ %span.required *
+ %td
+ %input{ type: 'text', name: 'phone', required: true, ng: { model: 'address.phone'} }
+ %tr
+ %td
+ = t('spree.city')
+ %span.required *
+ %td
+ %input{ type: 'text', name: 'city', required: true, ng: { model: 'address.city'} }
+ %tr
+ %td
+ = t('spree.zipcode')
+ %span.required *
+ %td
+ %input{ type: 'text', name: 'zipcode', required: true, ng: { model: 'address.zipcode'} }
+ %tr
+ %td
+ = t('spree.country')
+ %span.required *
+ %td
+ %select{name: 'country', required: true, ng: {model: 'address.country_id', options: 'country.id as country.name for country in availableCountries'}}
+ %option{value: ''}
+ = t('admin.customers.index.select_country')
+ %tr
+ %td
+ = t('spree.state')
+ %span.required *
+ %td
+ %select{name: 'state', required: true, ng: {model: 'address.state_id', options: 'state.id as state.name for state in states'}}
+ %option{value: ''}
+ = t('admin.customers.index.select_state')
+
+ .text-center
+ %input.button.red.icon-plus{ type: 'submit', value: 'Update Address'}
+
+
diff --git a/app/assets/javascripts/templates/partials/enterprise_details.html.haml b/app/assets/javascripts/templates/partials/enterprise_details.html.haml
index 46a37050e2..c454312f18 100644
--- a/app/assets/javascripts/templates/partials/enterprise_details.html.haml
+++ b/app/assets/javascripts/templates/partials/enterprise_details.html.haml
@@ -10,7 +10,7 @@
%span.filter-shopfront.property-selectors.pad-top
%ul.inline-block
- %li{"ng-repeat" => "property in enterprise.properties"}
+ %li{"ng-repeat" => "property in enterprise.supplied_properties"}
%a.button.tiny{"ng-bind" => "property.presentation"}
.about-container.pad-top
diff --git a/app/assets/stylesheets/admin/components/trial_progess_bar.sass b/app/assets/stylesheets/admin/components/trial_progess_bar.sass
index 37ef4642d9..cb6a648273 100644
--- a/app/assets/stylesheets/admin/components/trial_progess_bar.sass
+++ b/app/assets/stylesheets/admin/components/trial_progess_bar.sass
@@ -1,7 +1,8 @@
#trial_progress_bar
position: fixed
+ left: 0px
bottom: 0px
- width: 100%
+ width: 100vw
padding: 8px 10px
font-weight: bold
background-color: #5498da
diff --git a/app/assets/stylesheets/admin/validation.css.scss b/app/assets/stylesheets/admin/validation.css.scss
index c97335a6c4..f8fa261fdb 100644
--- a/app/assets/stylesheets/admin/validation.css.scss
+++ b/app/assets/stylesheets/admin/validation.css.scss
@@ -5,3 +5,8 @@ input.ng-invalid {
border: solid 1px red;
}
}
+
+select.ng-invalid {
+ border: solid 1px red;
+}
+
diff --git a/app/controllers/admin/contents_controller.rb b/app/controllers/admin/contents_controller.rb
index 87c86997d9..c8d04efebb 100644
--- a/app/controllers/admin/contents_controller.rb
+++ b/app/controllers/admin/contents_controller.rb
@@ -6,9 +6,9 @@ module Admin
{name: 'Producer signup page', preferences: [:producer_signup_pricing_table_html, :producer_signup_case_studies_html, :producer_signup_detail_html]},
{name: 'Hub signup page', preferences: [:hub_signup_pricing_table_html, :hub_signup_case_studies_html, :hub_signup_detail_html]},
{name: 'Group signup page', preferences: [:group_signup_pricing_table_html, :group_signup_case_studies_html, :group_signup_detail_html]},
- {name: 'Footer', preferences: [:footer_logo,
+ {name: 'Footer and External Links', preferences: [:footer_logo,
:footer_facebook_url, :footer_twitter_url, :footer_instagram_url, :footer_linkedin_url, :footer_googleplus_url, :footer_pinterest_url,
- :footer_email, :footer_links_md, :footer_about_url, :footer_tos_url]}]
+ :footer_email, :community_forum_url, :footer_links_md, :footer_about_url, :footer_tos_url]}]
end
def update
diff --git a/app/helpers/admin/business_model_configuration_helper.rb b/app/helpers/admin/business_model_configuration_helper.rb
index b1a2f1ac12..de17761924 100644
--- a/app/helpers/admin/business_model_configuration_helper.rb
+++ b/app/helpers/admin/business_model_configuration_helper.rb
@@ -16,6 +16,10 @@ module Admin
private
+ def free_use?
+ Spree::Config[:account_invoices_monthly_fixed] == 0 && Spree::Config[:account_invoices_monthly_rate] == 0
+ end
+
def fixed_description
fixed_amount = Spree::Money.new(Spree::Config[:account_invoices_monthly_fixed], {currency: Spree::Config[:currency]} ).rounded
monthly_bill_includes_fixed? ? "#{fixed_amount}" : ""
diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb
index 67105b6b34..b528eb84a4 100644
--- a/app/helpers/admin/injection_helper.rb
+++ b/app/helpers/admin/injection_helper.rb
@@ -39,6 +39,10 @@ module Admin
admin_inject_json_ams_array ngModule, "shops", @shops, Api::Admin::IdNameSerializer
end
+ def admin_inject_available_countries(ngModule='admin.customers')
+ admin_inject_json_ams_array ngModule, 'availableCountries', available_countries, Api::CountrySerializer
+ end
+
def admin_inject_hubs(opts={module: 'ofn.admin'})
admin_inject_json_ams_array opts[:module], "hubs", @hubs, Api::Admin::IdNameSerializer
end
diff --git a/app/models/calculator/flat_percent_per_item.rb b/app/models/calculator/flat_percent_per_item.rb
new file mode 100644
index 0000000000..17089b2f2c
--- /dev/null
+++ b/app/models/calculator/flat_percent_per_item.rb
@@ -0,0 +1,28 @@
+require_dependency 'spree/calculator'
+
+class Calculator::FlatPercentPerItem < Spree::Calculator
+ # Spree's FlatPercentItemTotal calculator sums all amounts, and then calculates a percentage
+ # on them.
+ # In the cart, we display line item individual amounts rounded, so to have consistent
+ # calculations we do the same internally. Here, we round adjustments at the individual
+ # item level first, then multiply by the item quantity.
+
+ preference :flat_percent, :decimal, :default => 0
+
+ attr_accessible :preferred_flat_percent
+
+ def self.description
+ I18n.t(:flat_percent_per_item)
+ end
+
+ def compute(object)
+ line_items_for(object).sum do |li|
+ unless li.price.present? && li.quantity.present?
+ raise ArgumentError, "object must respond to #price and #quantity"
+ end
+
+ value = (li.price * BigDecimal(self.preferred_flat_percent.to_s) / 100.0).round(2)
+ value * li.quantity
+ end
+ end
+end
diff --git a/app/models/content_configuration.rb b/app/models/content_configuration.rb
index e20279f5ee..fc35a2b0ba 100644
--- a/app/models/content_configuration.rb
+++ b/app/models/content_configuration.rb
@@ -34,6 +34,8 @@ class ContentConfiguration < Spree::Preferences::FileConfiguration
# Footer
preference :footer_logo, :file
has_attached_file :footer_logo, default_url: "/assets/ofn-logo-footer.png"
+
+ #Other
preference :footer_facebook_url, :string, default: "https://www.facebook.com/OpenFoodNet"
preference :footer_twitter_url, :string, default: "https://twitter.com/OpenFoodNet"
preference :footer_instagram_url, :string, default: ""
@@ -41,6 +43,7 @@ class ContentConfiguration < Spree::Preferences::FileConfiguration
preference :footer_googleplus_url, :string, default: ""
preference :footer_pinterest_url, :string, default: ""
preference :footer_email, :string, default: "hello@openfoodnetwork.org"
+ preference :community_forum_url, :string, default: "http://community.openfoodnetwork.org"
preference :footer_links_md, :text, default: <<-EOS
[Newsletter sign-up](/)
diff --git a/app/models/customer.rb b/app/models/customer.rb
index c208c619ba..3003acb848 100644
--- a/app/models/customer.rb
+++ b/app/models/customer.rb
@@ -6,8 +6,17 @@ class Customer < ActiveRecord::Base
has_many :orders, class_name: Spree::Order
before_destroy :check_for_orders
+ belongs_to :bill_address, foreign_key: :bill_address_id, class_name: Spree::Address
+ alias_attribute :billing_address, :bill_address
+ accepts_nested_attributes_for :bill_address
+
+ belongs_to :ship_address, foreign_key: :ship_address_id, class_name: Spree::Address
+ alias_attribute :shipping_address, :ship_address
+ accepts_nested_attributes_for :ship_address
+
before_validation :downcase_email
before_validation :empty_code
+ before_validation :set_unused_address_fields
validates :code, uniqueness: { scope: :enterprise_id, allow_nil: true }
validates :email, presence: true, uniqueness: { scope: :enterprise_id, message: I18n.t('validation_msg_is_associated_with_an_exising_customer') }
@@ -27,6 +36,11 @@ class Customer < ActiveRecord::Base
self.code = nil if code.blank?
end
+ def set_unused_address_fields
+ bill_address.firstname = bill_address.lastname = 'unused' if bill_address.present?
+ ship_address.firstname = ship_address.lastname = 'unused' if ship_address.present?
+ end
+
def associate_user
self.user = user || Spree::User.find_by_email(email)
end
diff --git a/app/models/producer_property.rb b/app/models/producer_property.rb
index 5b9fdd6d1c..bf1f083936 100644
--- a/app/models/producer_property.rb
+++ b/app/models/producer_property.rb
@@ -8,6 +8,15 @@ class ProducerProperty < ActiveRecord::Base
after_destroy :refresh_products_cache_from_destroy
+ scope :sold_by, ->(shop) {
+ joins(producer: {supplied_products: {variants: {exchanges: :order_cycle}}}).
+ merge(Exchange.outgoing).
+ merge(Exchange.to_enterprise(shop)).
+ merge(OrderCycle.active).
+ select('DISTINCT producer_properties.*')
+ }
+
+
def property_name
property.name if property
end
diff --git a/app/models/spree/gateway/pay_pal_express_decorator.rb b/app/models/spree/gateway/pay_pal_express_decorator.rb
deleted file mode 100644
index dc8239a107..0000000000
--- a/app/models/spree/gateway/pay_pal_express_decorator.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Spree
- class Gateway::PayPalExpress < Gateway
- # Something odd is happening with class inheritance here, this class (defined in spree_paypal_express gem)
- # doesn't seem to pick up attr_accessible from the Gateway class, so we redefine the attrs we need here
- attr_accessible :tag_list
- end
-end
diff --git a/app/models/spree/gateway_decorator.rb b/app/models/spree/gateway_decorator.rb
index 9125ef1d2d..bc4312961e 100644
--- a/app/models/spree/gateway_decorator.rb
+++ b/app/models/spree/gateway_decorator.rb
@@ -1,18 +1,5 @@
Spree::Gateway.class_eval do
- acts_as_taggable
-
- # Due to class load order, when config.cache_classes is enabled (ie. staging and production
- # environments), this association isn't inherited from PaymentMethod. As a result, creating
- # payment methods using payment gateways results in:
- # undefined method `association_class' for nil:NilClass
- # To avoid that, we redefine this association here.
-
- has_and_belongs_to_many :distributors, join_table: 'distributors_payment_methods', :class_name => 'Enterprise', foreign_key: 'payment_method_id', association_foreign_key: 'distributor_id'
-
-
# Default to live
preference :server, :string, :default => 'live'
preference :test_mode, :boolean, :default => false
-
- attr_accessible :tag_list
end
diff --git a/app/models/spree/payment_method_decorator.rb b/app/models/spree/payment_method_decorator.rb
index 77e22c591f..4a718dfc16 100644
--- a/app/models/spree/payment_method_decorator.rb
+++ b/app/models/spree/payment_method_decorator.rb
@@ -1,7 +1,6 @@
Spree::PaymentMethod.class_eval do
acts_as_taggable
- # See gateway_decorator.rb when modifying this association
has_and_belongs_to_many :distributors, join_table: 'distributors_payment_methods', :class_name => 'Enterprise', association_foreign_key: 'distributor_id'
attr_accessible :distributor_ids, :tag_list
@@ -45,26 +44,20 @@ Spree::PaymentMethod.class_eval do
def has_distributor?(distributor)
self.distributors.include?(distributor)
end
-end
-# Ensure that all derived classes also allow distributor_ids
-Spree::Gateway.providers.each do |p|
- p.attr_accessible :distributor_ids
- p.instance_eval do
- def clean_name
- case name
- when "Spree::PaymentMethod::Check"
- "Cash/EFT/etc. (payments for which automatic validation is not required)"
- when "Spree::Gateway::Migs"
- "MasterCard Internet Gateway Service (MIGS)"
- when "Spree::Gateway::Pin"
- "Pin Payments"
- when "Spree::Gateway::PayPalExpress"
- "PayPal Express"
- else
- i = name.rindex('::') + 2
- name[i..-1]
- end
+ def self.clean_name
+ case name
+ when "Spree::PaymentMethod::Check"
+ "Cash/EFT/etc. (payments for which automatic validation is not required)"
+ when "Spree::Gateway::Migs"
+ "MasterCard Internet Gateway Service (MIGS)"
+ when "Spree::Gateway::Pin"
+ "Pin Payments"
+ when "Spree::Gateway::PayPalExpress"
+ "PayPal Express"
+ else
+ i = name.rindex('::') + 2
+ name[i..-1]
end
end
end
diff --git a/app/models/spree/property_decorator.rb b/app/models/spree/property_decorator.rb
index f6eaefc75f..50e450a03b 100644
--- a/app/models/spree/property_decorator.rb
+++ b/app/models/spree/property_decorator.rb
@@ -1,11 +1,22 @@
module Spree
Property.class_eval do
+ has_many :producer_properties
+
scope :applied_by, ->(enterprise) {
select('DISTINCT spree_properties.*').
joins(:product_properties).
where('spree_product_properties.product_id IN (?)', enterprise.supplied_product_ids)
}
+ scope :sold_by, ->(shop) {
+ joins(products: {variants: {exchanges: :order_cycle}}).
+ merge(Exchange.outgoing).
+ merge(Exchange.to_enterprise(shop)).
+ merge(OrderCycle.active).
+ select('DISTINCT spree_properties.*')
+ }
+
+
after_save :refresh_products_cache
# When a Property is destroyed, dependent-destroy will destroy all ProductProperties,
diff --git a/app/overrides/spree/layouts/admin/add_trial_progress_bar.html.haml.deface b/app/overrides/spree/layouts/admin/add_trial_progress_bar.html.haml.deface
deleted file mode 100644
index eb9b44ad10..0000000000
--- a/app/overrides/spree/layouts/admin/add_trial_progress_bar.html.haml.deface
+++ /dev/null
@@ -1,5 +0,0 @@
-/ insert_top "[data-hook='admin_footer_scripts']"
-
-- enterprise = spree_current_user.enterprises.first if OpenFoodNetwork::Permissions.new(spree_current_user).manages_one_enterprise?
-
-= render 'spree/admin/shared/trial_progress_bar', enterprise: enterprise
\ No newline at end of file
diff --git a/app/serializers/api/admin/customer_serializer.rb b/app/serializers/api/admin/customer_serializer.rb
index 4634625c11..1c9d19306d 100644
--- a/app/serializers/api/admin/customer_serializer.rb
+++ b/app/serializers/api/admin/customer_serializer.rb
@@ -1,5 +1,8 @@
class Api::Admin::CustomerSerializer < ActiveModel::Serializer
- attributes :id, :email, :enterprise_id, :user_id, :code, :tags, :tag_list
+ attributes :id, :email, :enterprise_id, :user_id, :code, :tags, :tag_list, :name
+
+ has_one :ship_address, serializer: Api::AddressSerializer
+ has_one :bill_address, serializer: Api::AddressSerializer
def tag_list
object.tag_list.join(",")
diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb
index d30c2d8677..f68c8be679 100644
--- a/app/serializers/api/enterprise_serializer.rb
+++ b/app/serializers/api/enterprise_serializer.rb
@@ -21,7 +21,8 @@ end
class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer
attributes :orders_close_at, :active
- has_many :properties, serializer: Api::PropertySerializer
+ has_many :supplied_properties, serializer: Api::PropertySerializer
+ has_many :distributed_properties, serializer: Api::PropertySerializer
def orders_close_at
options[:data].earliest_closing_times[object.id]
@@ -31,13 +32,22 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer
options[:data].active_distributors.andand.include? object
end
- def properties
+ def supplied_properties
# This results in 3 queries per enterprise
product_properties = Spree::Property.applied_by(object)
producer_properties = object.properties
OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties
end
+
+ def distributed_properties
+ # This results in 3 queries per enterprise
+ product_properties = Spree::Property.sold_by(object)
+ ids = ProducerProperty.sold_by(object).pluck(:property_id)
+ producer_properties = Spree::Property.where(id: ids)
+
+ OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties
+ end
end
class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml
index 98e8b3c414..7e08c2e4b3 100644
--- a/app/views/admin/customers/index.html.haml
+++ b/app/views/admin/customers/index.html.haml
@@ -12,6 +12,7 @@
= admin_inject_column_preferences module: 'admin.customers'
= admin_inject_shops
+= admin_inject_available_countries
%div{ ng: { controller: 'customersCtrl' } }
.row.filters
@@ -37,6 +38,7 @@
%img.spinner{ src: "/assets/spinning-circles.svg" }
%h1
=t :loading_customers
+
.row{ :class => "sixteen columns alpha", 'ng-show' => '!RequestMonitor.loading && filteredCustomers.length == 0'}
%h1#no_results
=t :no_customers_found
@@ -48,18 +50,25 @@
%table.index#customers
%col.email{ width: "20%", 'ng-show' => 'columns.email.visible' }
- %col.code{ width: "20%", 'ng-show' => 'columns.code.visible' }
- %col.tags{ width: "50%", 'ng-show' => 'columns.tags.visible' }
+ %col.name{ width: "20%", 'ng-show' => 'columns.name.visible' }
+ %col.code{ width: "10%", 'ng-show' => 'columns.code.visible' }
+ %col.tags{ width: "20%", 'ng-show' => 'columns.tags.visible' }
+ %col.bill_address{ width: "10%", 'ng-show' => 'columns.bill_address.visible' }
+ %col.ship_address{ width: "10%", 'ng-show' => 'columns.ship_address.visible' }
%col.actions{ width: "10%"}
%thead
%tr{ ng: { controller: "ColumnsCtrl" } }
-# %th.bulk
-# %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" }
%th.email{ 'ng-show' => 'columns.email.visible' }
- %a{ :href => '', 'ng-click' => "predicate = 'customer.email'; reverse = !reverse" } Email
+ %a{ :href => '', 'ng-click' => "predicate = 'customer.email'; reverse = !reverse" }=t('admin.email')
+ %th.name{ 'ng-show' => 'columns.name.visible' }
+ %a{ :href => '', 'ng-click' => "predicate = 'customer.name'; reverse = !reverse" }=t('admin.name')
%th.code{ 'ng-show' => 'columns.code.visible' }
- %a{ :href => '', 'ng-click' => "predicate = 'customer.code'; reverse = !reverse" } Code
- %th.tags{ 'ng-show' => 'columns.tags.visible' } Tags
+ %a{ :href => '', 'ng-click' => "predicate = 'customer.code'; reverse = !reverse" }=t('admin.customers.index.code')
+ %th.tags{ 'ng-show' => 'columns.tags.visible' }=t('admin.tags')
+ %th.bill_address{ 'ng-show' => 'columns.bill_address.visible' }=t('admin.customers.index.bill_address')
+ %th.ship_address{ 'ng-show' => 'columns.ship_address.visible' }=t('admin.customers.index.ship_address')
%th.actions
Ask?
%input{ :type => 'checkbox', 'ng-model' => "confirmDelete" }
@@ -67,6 +76,8 @@
-# %td.bulk
-# %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'customer.checked' }
%td.email{ 'ng-show' => 'columns.email.visible', "ng-bind" => '::customer.email' }
+ %td.name{ 'ng-show' => 'columns.name.visible'}
+ %input{ type: 'text', name: 'name', ng: { model: 'customer.name' }, 'obj-for-update' => 'customer', 'attr-for-update' => 'name'}
%td.code{ 'ng-show' => 'columns.code.visible' }
%input{ type: 'text', name: 'code', ng: {model: 'customer.code', change: 'checkForDuplicateCodes()'}, "obj-for-update" => "customer", "attr-for-update" => "code" }
%i.icon-warning-sign{ ng: {if: 'duplicate'} }
@@ -74,6 +85,10 @@
%td.tags{ 'ng-show' => 'columns.tags.visible' }
.tag_watcher{ 'obj-for-update' => "customer", "attr-for-update" => "tag_list"}
%tags_with_translation{ object: 'customer', 'find-tags' => 'findTags(query)' }
+ %td.bill_address{ 'ng-show' => 'columns.bill_address.visible' }
+ %a{ id: 'bill-address-link', href: '#', "ng-bind" => "customer.bill_address ? customer.bill_address.address1 : '#{t('admin.customers.index.edit')}' | limitTo: 15", 'edit-address-dialog' => true }
+ %td.ship_address{ 'ng-show' => 'columns.ship_address.visible' }
+ %a{ id: 'ship-address-link', href: '#', "ng-bind" => "customer.ship_address ? customer.ship_address.address1 : '#{t('admin.customers.index.edit')}' | limitTo: 15", 'edit-address-dialog' => true }
%td.actions
%a{ 'ng-click' => "deleteCustomer(customer)", :class => "delete-customer icon-trash no-text" }
diff --git a/app/views/admin/enterprises/_change_type_form.html.haml b/app/views/admin/enterprises/_change_type_form.html.haml
index 6474af1344..ac5ca55821 100644
--- a/app/views/admin/enterprises/_change_type_form.html.haml
+++ b/app/views/admin/enterprises/_change_type_form.html.haml
@@ -50,7 +50,9 @@
-# At the end of your trial, there is a one-off $200 fee to fully activate your account. Then you will be billed for 2% of your actual transactions, capped at $50 a month (so if you don’t sell anything you don’t pay anything, but you never pay more than $50 a month).
- else
- .shop_profile.option.one-third.column.alpha
+ .two.columns.alpha
+
+ .shop_profile.option.six.columns
%a.full-width.button.selector{ ng: { click: "sells='none'", class: "{selected: sells=='none'}" } }
.top
%h3 Profile Only
@@ -59,25 +61,29 @@
%p.description
People can find and contact you on the Open Food Network. Your enterprise will be visible on the map, and will be searchable in listings.
- .full_hub.option.one-third.column
+ .full_hub.option.six.columns
%a.full-width.button.selector{ ng: { click: "sells='any'", class: "{selected: sells=='any'}" } }
.top
%h3 Hub Shop
%p Sell produce from others
.bottom
%monthly-pricing-description{ joiner: "newline" }
-
%p.description
Your enterprise is the backbone of your local food system. You aggregate produce from other enterprises and can sell it through your shop on the Open Food Network.
-
+ .two.columns.omega
+
.row
.sixteen.columns.alpha
%span.error{ ng: { show: "(change_type.sells.$error.required || change_type.sells.$error.pattern) && submitted" } }
Please choose one of the options above.
- if @enterprise.sells == 'unspecified' && @enterprise.shop_trial_start_date.nil?
- %input.button.big{ type: 'submit', value: 'Start 30 Day Trial', ng: { click: "submit(change_type)", show: "sells=='own' || sells=='any'" } }
- %input.button.big{ type: 'submit', value: 'Select and continue', ng: { click: "submit(change_type)", hide: "sells=='own' || sells=='any'" } }
+ -if free_use?
+ %input.button.big{ type: 'submit', value: 'Select and continue', ng: { click: "submit(change_type)" } }
+ - else
+ - trial_length = Spree::Config[:shop_trial_length_days]
+ %input.button.big{ type: 'submit', value: "Start #{trial_length}-Day Shop Trial", ng: { click: "submit(change_type)", show: "sells=='own' || sells=='any'" } }
+ %input.button.big{ type: 'submit', value: 'Select and continue', ng: { click: "submit(change_type)", hide: "sells=='own' || sells=='any'" } }
- elsif @enterprise.sells == 'unspecified'
%input.button.big{ type: 'submit', value: 'Select and continue', ng: { click: "submit(change_type)" } }
- else
diff --git a/app/views/admin/shared/_user_guide_link.html.haml b/app/views/admin/shared/_user_guide_link.html.haml
index bfae6002e7..6081422aba 100644
--- a/app/views/admin/shared/_user_guide_link.html.haml
+++ b/app/views/admin/shared/_user_guide_link.html.haml
@@ -1 +1 @@
-= button_link_to "User Guide", "http://www.openfoodnetwork.org/platform/user-guide/", :icon => 'icon-external-link', :id => 'user_guide_link', target: '_blank'
+= button_link_to "User Guide", "http://www.openfoodnetwork.org/platform/user-guide/", :icon => 'icon-external-link', target: '_blank'
diff --git a/app/views/enterprise_mailer/welcome.html.haml b/app/views/enterprise_mailer/welcome.html.haml
index 97b350de45..33db02ed98 100644
--- a/app/views/enterprise_mailer/welcome.html.haml
+++ b/app/views/enterprise_mailer/welcome.html.haml
@@ -13,7 +13,7 @@
= t :email_admin_html, link: link_to('Admin Panel', spree.admin_url)
%p
- = t :email_community_html, link: link_to('Join the community.', 'http://community.openfoodnetwork.org/')
+ = t :email_community_html, link: link_to(t(:join_the_community), ContentConfig.community_forum_url)
%p
= t :email_help
diff --git a/app/views/home/_fat.html.haml b/app/views/home/_fat.html.haml
index 4e90771cc1..d5703f84d8 100644
--- a/app/views/home/_fat.html.haml
+++ b/app/views/home/_fat.html.haml
@@ -4,9 +4,13 @@
%label
= t :hubs_buy
.trans-sentence
- %span.fat-taxons{"ng-repeat" => "taxon in hub.taxons"}
- %render-svg{path: "{{taxon.icon}}"}
- %span{"ng-bind" => "::taxon.name"}
+ %div
+ %span.fat-taxons{"ng-repeat" => "taxon in hub.taxons"}
+ %render-svg{path: "{{taxon.icon}}"}
+ %span{"ng-bind" => "::taxon.name"}
+ %div
+ %span.fat-properties{"ng-repeat" => "property in hub.distributed_properties"}
+ %span{"ng-bind" => "property.presentation"}
%div.show-for-medium-up{"ng-if" => "::hub.taxons.length==0"}
.columns.small-12.medium-3.large-2.fat
diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml
index 28ed0c985f..8d68a2b84f 100644
--- a/app/views/producers/_fat.html.haml
+++ b/app/views/producers/_fat.html.haml
@@ -21,7 +21,7 @@
%render-svg{path: "{{taxon.icon}}"}
%span{"ng-bind" => "::taxon.name"}
%div
- %span.fat-properties{"ng-repeat" => "property in producer.properties"}
+ %span.fat-properties{"ng-repeat" => "property in producer.supplied_properties"}
%span{"ng-bind" => "property.presentation"}
%div.show-for-medium-up{"ng-if" => "producer.supplied_taxons.length==0"}
diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml
index 91c0934e21..76377f32e2 100644
--- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml
+++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml
@@ -1,11 +1,11 @@
- content_for :page_title do
- %h1
- = @enterprise.name
- %span.small<
- = "(#{enterprise_type_name(@enterprise)})"
+ = @enterprise.name
+ %span.small<
+ = "(#{enterprise_type_name(@enterprise)})"
- content_for :page_actions do
- = render 'admin/shared/user_guide_link'
+ %li#user_guide_link
+ = render 'admin/shared/user_guide_link'
:javascript
function toggleType(){
@@ -17,7 +17,7 @@
}
$("#package_selection").slideToggle()
}
- #package_button
+ %li#package_button
%button#toggle_type{ onClick: 'toggleType()' }
= t "change_package"
%i.icon-chevron-down
@@ -98,3 +98,5 @@
%a.button.bottom{href: main_app.admin_order_cycles_path}
= t "manage_order_cycles"
%span.icon-arrow-right
+
+= render 'spree/admin/shared/trial_progress_bar', enterprise: @enterprise
diff --git a/app/views/spree/user_mailer/signup_confirmation.html.haml b/app/views/spree/user_mailer/signup_confirmation.html.haml
index e45b99a000..273d497c5d 100644
--- a/app/views/spree/user_mailer/signup_confirmation.html.haml
+++ b/app/views/spree/user_mailer/signup_confirmation.html.haml
@@ -21,9 +21,7 @@
%p.lead
= t :email_signup_text
%p
- = t :email_signup_help_html
- %a{:href => "mailto:hello@openfoodnetwork.org", :target => "_blank"}
- hello@openfoodnetwork.org
+ = t :email_signup_help_html, email: mail_to(ContentConfig.footer_email)
= render 'shared/mailers/signoff'
diff --git a/config/application.rb b/config/application.rb
index 9abd0a3b9b..f9d6151e10 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -28,7 +28,7 @@ module Openfoodnetwork
initializer "spree.register.calculators" do |app|
app.config.spree.calculators.shipping_methods << OpenFoodNetwork::Calculator::Weight
- app.config.spree.calculators.enterprise_fees = [Spree::Calculator::FlatPercentItemTotal,
+ app.config.spree.calculators.enterprise_fees = [Calculator::FlatPercentPerItem,
Spree::Calculator::FlatRate,
Spree::Calculator::FlexiRate,
Spree::Calculator::PerItem,
diff --git a/config/initializers/spree.rb b/config/initializers/spree.rb
index 92ba3db249..30182ef63a 100644
--- a/config/initializers/spree.rb
+++ b/config/initializers/spree.rb
@@ -9,6 +9,9 @@
require 'spree/product_filters'
+require "#{Rails.root}/app/models/spree/payment_method_decorator"
+require "#{Rails.root}/app/models/spree/gateway_decorator"
+
Spree.config do |config|
config.shipping_instructions = true
config.address_requires_state = true
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 386f44ae8b..1d8707a7b9 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -118,6 +118,16 @@ en:
add_a_new_customer_for: Add a new customer for %{shop_name}
code: Code
duplicate_code: "This code is used already."
+ bill_address: "Billing Address"
+ ship_address: "Shipping Address"
+ update_address_success: 'Address updated successfully.'
+ update_address_error: 'Sorry! Please input all of the required fields!'
+ edit_bill_address: 'Edit Billing Address'
+ edit_ship_address: 'Edit Shipping Address'
+ required_fileds: 'Required fields are denoted with an asterisk '
+ select_country: 'Select Country'
+ select_state: 'Select State'
+ edit: 'Edit'
products:
bulk_edit:
@@ -411,6 +421,7 @@ en:
email_admin_html: "You can manage your account by logging into the %{link} or by clicking on the cog in the top right hand side of the homepage, and selecting Administration."
email_community_html: "We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next.
%{link}"
+ join_community: "Join the community"
email_help: "If you have any difficulties, check out our FAQs, browse the forum or post a 'Support' topic and someone will help you out!"
email_confirmation_greeting: "Hi, %{contact}!"
email_confirmation_profile_created: "A profile for %{name} has been successfully created!
@@ -458,7 +469,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
email_signup_text: "Thanks for joining the network.
If you are a customer, we look forward to introducing you to many fantastic farmers, wonderful food hubs and delicious food!
If you are a producer or food enterprise, we are excited to have you as a part of the network."
- email_signup_help_html: "We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at"
+ email_signup_help_html: "We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at %{email}"
producer_mail_greeting: "Dear"
producer_mail_text_before: "We now have all the consumer orders for the next food drop."
@@ -900,6 +911,7 @@ Please follow the instructions there to make your enterprise visible on the Open
tax_category: "Tax Category"
calculator: "Calculator"
calculator_values: "Calculator values"
+ flat_percent_per_item: "Flat Percent (per item)"
new_order_cycles: "New Order Cycles"
select_a_coordinator_for_your_order_cycle: "Select a coordinator for your order cycle"
edit_order_cycle: "Edit Order Cycle"
diff --git a/db/migrate/20160713003535_add_bill_address_and_ship_address_to_customer.rb b/db/migrate/20160713003535_add_bill_address_and_ship_address_to_customer.rb
new file mode 100644
index 0000000000..306276bcc0
--- /dev/null
+++ b/db/migrate/20160713003535_add_bill_address_and_ship_address_to_customer.rb
@@ -0,0 +1,12 @@
+class AddBillAddressAndShipAddressToCustomer < ActiveRecord::Migration
+ def change
+ add_column :customers, :bill_address_id, :integer
+ add_column :customers, :ship_address_id, :integer
+
+ add_index :customers, :bill_address_id
+ add_index :customers, :ship_address_id
+
+ add_foreign_key :customers, :spree_addresses, column: :bill_address_id
+ add_foreign_key :customers, :spree_addresses, column: :ship_address_id
+ end
+end
diff --git a/db/migrate/20160713013358_add_name_to_customer.rb b/db/migrate/20160713013358_add_name_to_customer.rb
new file mode 100644
index 0000000000..832816657e
--- /dev/null
+++ b/db/migrate/20160713013358_add_name_to_customer.rb
@@ -0,0 +1,5 @@
+class AddNameToCustomer < ActiveRecord::Migration
+ def change
+ add_column :customers, :name, :string
+ end
+end
diff --git a/db/migrate/20160819065331_swap_calculator_to_flat_percent_per_item.rb b/db/migrate/20160819065331_swap_calculator_to_flat_percent_per_item.rb
new file mode 100644
index 0000000000..eb8e206e8f
--- /dev/null
+++ b/db/migrate/20160819065331_swap_calculator_to_flat_percent_per_item.rb
@@ -0,0 +1,29 @@
+class SwapCalculatorToFlatPercentPerItem < ActiveRecord::Migration
+ class Spree::Calculator < ActiveRecord::Base
+ end
+
+ def up
+ Spree::Calculator.where(calculable_type: "EnterpriseFee", type: 'Spree::Calculator::FlatPercentItemTotal').each do |c|
+ swap_calculator_type c, 'Calculator::FlatPercentPerItem'
+ end
+ end
+
+ def down
+ Spree::Calculator.where(calculable_type: "EnterpriseFee", type: 'Spree::Calculator::FlatPercentPerItem').each do |c|
+ swap_calculator_type c, 'Calculator::FlatPercentItemTotal'
+ end
+ end
+
+
+ private
+
+ def swap_calculator_type(calculator, to_class)
+ value = calculator.preferred_flat_percent
+
+ calculator.type = to_class
+ calculator.save
+
+ calculator = Spree::Calculator.find calculator.id
+ calculator.preferred_flat_percent = value
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d0403533fd..48b07e3cdb 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20160707023818) do
+ActiveRecord::Schema.define(:version => 20160819065331) do
create_table "account_invoices", :force => true do |t|
t.integer "user_id", :null => false
@@ -79,16 +79,21 @@ ActiveRecord::Schema.define(:version => 20160707023818) do
add_index "coordinator_fees", ["order_cycle_id"], :name => "index_coordinator_fees_on_order_cycle_id"
create_table "customers", :force => true do |t|
- t.string "email", :null => false
- t.integer "enterprise_id", :null => false
+ t.string "email", :null => false
+ t.integer "enterprise_id", :null => false
t.string "code"
t.integer "user_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
+ t.integer "bill_address_id"
+ t.integer "ship_address_id"
+ t.string "name"
end
+ add_index "customers", ["bill_address_id"], :name => "index_customers_on_bill_address_id"
add_index "customers", ["email"], :name => "index_customers_on_email"
add_index "customers", ["enterprise_id", "code"], :name => "index_customers_on_enterprise_id_and_code", :unique => true
+ add_index "customers", ["ship_address_id"], :name => "index_customers_on_ship_address_id"
add_index "customers", ["user_id"], :name => "index_customers_on_user_id"
create_table "delayed_jobs", :force => true do |t|
@@ -577,9 +582,9 @@ ActiveRecord::Schema.define(:version => 20160707023818) do
t.string "email"
t.text "special_instructions"
t.integer "distributor_id"
+ t.integer "order_cycle_id"
t.string "currency"
t.string "last_ip_address"
- t.integer "order_cycle_id"
t.integer "cart_id"
t.integer "customer_id"
end
@@ -1111,6 +1116,8 @@ ActiveRecord::Schema.define(:version => 20160707023818) do
add_foreign_key "coordinator_fees", "order_cycles", name: "coordinator_fees_order_cycle_id_fk"
add_foreign_key "customers", "enterprises", name: "customers_enterprise_id_fk"
+ add_foreign_key "customers", "spree_addresses", name: "customers_bill_address_id_fk", column: "bill_address_id"
+ add_foreign_key "customers", "spree_addresses", name: "customers_ship_address_id_fk", column: "ship_address_id"
add_foreign_key "customers", "spree_users", name: "customers_user_id_fk", column: "user_id"
add_foreign_key "distributors_payment_methods", "enterprises", name: "distributors_payment_methods_distributor_id_fk", column: "distributor_id"
diff --git a/lib/open_food_network/column_preference_defaults.rb b/lib/open_food_network/column_preference_defaults.rb
index 09dc197832..41eef63c09 100644
--- a/lib/open_food_network/column_preference_defaults.rb
+++ b/lib/open_food_network/column_preference_defaults.rb
@@ -27,9 +27,12 @@ module OpenFoodNetwork
def customers_index_columns
node = 'admin.customers.index'
{
- email: { name: I18n.t("admin.email"), visible: true },
- code: { name: I18n.t("#{node}.code"), visible: true },
- tags: { name: I18n.t("admin.tags"), visible: true }
+ email: { name: I18n.t("admin.email"), visible: true },
+ name: { name: I18n.t("admin.name"), visible: true },
+ code: { name: I18n.t("#{node}.code"), visible: true },
+ tags: { name: I18n.t("admin.tags"), visible: true },
+ bill_address: { name: I18n.t("#{node}.bill_address"), visible: true },
+ ship_address: { name: I18n.t("#{node}.ship_address"), visible: true }
}
end
diff --git a/lib/open_food_network/enterprise_fee_calculator.rb b/lib/open_food_network/enterprise_fee_calculator.rb
index 7222abad1c..e72f643ff2 100644
--- a/lib/open_food_network/enterprise_fee_calculator.rb
+++ b/lib/open_food_network/enterprise_fee_calculator.rb
@@ -104,8 +104,7 @@ module OpenFoodNetwork
def calculate_fee_for(variant, enterprise_fee)
# Spree's Calculator interface accepts Orders or LineItems,
# so we meet that interface with a struct.
- # Amount is faked, this is a method on LineItem
- line_item = OpenStruct.new variant: variant, quantity: 1, amount: variant.price
+ line_item = OpenStruct.new variant: variant, quantity: 1, price: variant.price, amount: variant.price
enterprise_fee.compute_amount(line_item)
end
diff --git a/spec/factories.rb b/spec/factories.rb
index 3b0c548923..9875d22652 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -270,6 +270,7 @@ FactoryGirl.define do
enterprise
code { SecureRandom.base64(150) }
user
+ bill_address { create(:address) }
end
factory :billable_period do
diff --git a/spec/features/admin/customers_spec.rb b/spec/features/admin/customers_spec.rb
index 89893860df..07352ad151 100644
--- a/spec/features/admin/customers_spec.rb
+++ b/spec/features/admin/customers_spec.rb
@@ -80,8 +80,10 @@ feature 'Customers' do
within "tr#c_#{customer1.id}" do
fill_in "code", with: "new-customer-code"
expect(page).to have_css "input[name=code].update-pending"
- end
- within "tr#c_#{customer1.id}" do
+
+ fill_in "name", with: "customer abc"
+ expect(page).to have_css "input[name=name].update-pending"
+
find(:css, "tags-input .tags input").set "awesome\n"
expect(page).to have_css ".tag_watcher.update-pending"
end
@@ -89,18 +91,22 @@ feature 'Customers' do
# Every says it updated
expect(page).to have_css "input[name=code].update-success"
+ expect(page).to have_css "input[name=name].update-success"
expect(page).to have_css ".tag_watcher.update-success"
# And it actually did
expect(customer1.reload.code).to eq "new-customer-code"
+ expect(customer1.reload.name).to eq "customer abc"
expect(customer1.tag_list).to eq ["awesome"]
# Clearing attributes
within "tr#c_#{customer1.id}" do
fill_in "code", with: ""
expect(page).to have_css "input[name=code].update-pending"
- end
- within "tr#c_#{customer1.id}" do
+
+ fill_in "name", with: ""
+ expect(page).to have_css "input[name=name].update-pending"
+
find("tags-input li.tag-item a.remove-button").trigger('click')
expect(page).to have_css ".tag_watcher.update-pending"
end
@@ -108,10 +114,12 @@ feature 'Customers' do
# Every says it updated
expect(page).to have_css "input[name=code].update-success"
+ expect(page).to have_css "input[name=name].update-success"
expect(page).to have_css ".tag_watcher.update-success"
# And it actually did
expect(customer1.reload.code).to be nil
+ expect(customer1.reload.name).to eq ''
expect(customer1.tag_list).to eq []
end
@@ -142,6 +150,52 @@ feature 'Customers' do
expect(customer2.reload.code).to be nil
end
+ describe 'updating a customer addresses' do
+ before do
+ select2_select managed_distributor2.name, from: "shop_id"
+ end
+
+ it 'updates the existing billing address' do
+ expect(page).to have_content 'BILLING ADDRESS'
+
+ first('#bill-address-link').click
+
+ expect(page).to have_content 'Edit Billing Address'
+ fill_in 'address1', with: "New Address1"
+ click_button 'Update Address'
+
+ expect(page).to have_content 'Address updated successfully.'
+ expect(page).to have_link 'New Address1'
+
+ expect(customer4.reload.bill_address.address1).to eq 'New Address1'
+ end
+
+ it 'creates a new shipping address' do
+ expect(page).to have_content 'SHIPPING ADDRESS'
+
+ first('#ship-address-link').click
+ expect(page).to have_content 'Edit Shipping Address'
+
+ fill_in 'address1', with: "New Address1"
+ fill_in 'phone', with: "12345678"
+ fill_in 'city', with: "Melbourne"
+ fill_in 'zipcode', with: "3000"
+
+ select 'Australia', from: 'country'
+ select 'Victoria', from: 'state'
+ click_button 'Update Address'
+
+ expect(page).to have_content 'Address updated successfully.'
+ expect(page).to have_link 'New Address1'
+
+ ship_address = customer4.reload.ship_address
+
+ expect(ship_address.address1).to eq 'New Address1'
+ expect(ship_address.phone).to eq '12345678'
+ expect(ship_address.city).to eq 'Melbourne'
+ end
+ end
+
describe "creating a new customer" do
context "when no shop has been selected" do
it "asks the user to select a shop" do
diff --git a/spec/features/admin/enterprise_fees_spec.rb b/spec/features/admin/enterprise_fees_spec.rb
index 7025b54766..cd24e8e3a3 100644
--- a/spec/features/admin/enterprise_fees_spec.rb
+++ b/spec/features/admin/enterprise_fees_spec.rb
@@ -77,13 +77,13 @@ feature %q{
page.should have_select "enterprise_fee_set_collection_attributes_0_fee_type", selected: 'Admin'
page.should have_selector "input[value='Greetings!']"
page.should have_select 'enterprise_fee_set_collection_attributes_0_tax_category_id', selected: 'Inherit From Product'
- page.should have_selector "option[selected]", text: 'Flat Percent'
+ page.should have_selector "option[selected]", text: 'Flat Percent (per item)'
fee.reload
fee.enterprise.should == enterprise
fee.name.should == 'Greetings!'
fee.fee_type.should == 'admin'
- fee.calculator_type.should == "Spree::Calculator::FlatPercentItemTotal"
+ fee.calculator_type.should == "Calculator::FlatPercentPerItem"
# Sets tax_category and inherits_tax_category
fee.tax_category.should == nil
diff --git a/spec/features/consumer/shopping/cart_spec.rb b/spec/features/consumer/shopping/cart_spec.rb
index ed573d0460..b604769502 100644
--- a/spec/features/consumer/shopping/cart_spec.rb
+++ b/spec/features/consumer/shopping/cart_spec.rb
@@ -10,20 +10,42 @@ feature "full-page cart", js: true do
let!(:zone) { create(:zone_with_member) }
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) }
let(:supplier) { create(:supplier_enterprise) }
- let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) }
- let(:enterprise_fee) { create(:enterprise_fee, amount: 11.00, tax_category: product.tax_category) }
- let(:product) { create(:taxed_product, supplier: supplier, zone: zone, price: 110.00, tax_rate_amount: 0.1) }
- let(:variant) { product.variants.first }
+ let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product_tax.variants.first, product_fee.variants.first]) }
+ let(:enterprise_fee) { create(:enterprise_fee, amount: 11.00, tax_category: product_tax.tax_category) }
+ let(:product_tax) { create(:taxed_product, supplier: supplier, zone: zone, price: 110.00, tax_rate_amount: 0.1) }
+ let(:product_fee) { create(:simple_product, supplier: supplier, price: 0.86, on_hand: 100) }
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) }
before do
- add_enterprise_fee enterprise_fee
set_order order
- add_product_to_cart
- visit spree.cart_path
+ end
+
+ describe "fees" do
+ let(:percentage_fee) { create(:enterprise_fee, calculator: Calculator::FlatPercentPerItem.new(preferred_flat_percent: 20)) }
+
+ before do
+ add_enterprise_fee percentage_fee
+ add_product_to_cart order, product_fee, quantity: 8
+ visit spree.cart_path
+ end
+
+ it "rounds fee calculations correctly" do
+ # $0.86 + 20% = $1.032
+ # Fractional cents should be immediately rounded down and not carried through
+ expect(page).to have_selector '.cart-item-price', text: '$1.03'
+ expect(page).to have_selector '.cart-item-total', text: '$8.24'
+ expect(page).to have_selector '.order-total.item-total', text: '$8.24'
+ expect(page).to have_selector '.order-total.grand-total', text: '$8.24'
+ end
end
describe "tax" do
+ before do
+ add_enterprise_fee enterprise_fee
+ add_product_to_cart order, product_tax
+ visit spree.cart_path
+ end
+
it "shows the total tax for the order, including product tax and tax on fees" do
page.should have_selector '.tax-total', text: '11.00' # 10 + 1
end
@@ -31,12 +53,15 @@ feature "full-page cart", js: true do
describe "updating quantities with insufficient stock available" do
let(:li) { order.line_items(true).last }
+ let(:variant) { product_tax.variants.first }
before do
- variant.update_attributes! on_hand: 2
+ add_product_to_cart order, product_tax
end
it "prevents me from entering an invalid value" do
+ # Given we have 2 on hand, and we've loaded the page after that fact
+ variant.update_attributes! on_hand: 2
visit spree.cart_path
accept_alert 'Insufficient stock available, only 2 remaining' do
@@ -47,6 +72,10 @@ feature "full-page cart", js: true do
end
it "shows the quantities saved, not those submitted" do
+ # Given we load the page with 3 on hand, then the number available drops to 2
+ visit spree.cart_path
+ variant.update_attributes! on_hand: 2
+
fill_in "order_line_items_attributes_0_quantity", with: '4'
click_button 'Update'
diff --git a/spec/features/consumer/shopping/checkout_auth_spec.rb b/spec/features/consumer/shopping/checkout_auth_spec.rb
index 0b0683def0..e23e4c15a8 100644
--- a/spec/features/consumer/shopping/checkout_auth_spec.rb
+++ b/spec/features/consumer/shopping/checkout_auth_spec.rb
@@ -18,7 +18,7 @@ feature "As a consumer I want to check out my cart", js: true do
before do
set_order order
- add_product_to_cart
+ add_product_to_cart order, product
end
it "does not not render the login form when logged in" do
diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb
index 10147b21a4..6055cb47d1 100644
--- a/spec/features/consumer/shopping/checkout_spec.rb
+++ b/spec/features/consumer/shopping/checkout_spec.rb
@@ -23,7 +23,7 @@ feature "As a consumer I want to check out my cart", js: true do
add_enterprise_fee enterprise_fee
set_order order
- add_product_to_cart
+ add_product_to_cart order, product
end
describe "with shipping and payment methods" do
diff --git a/spec/features/consumer/shopping/variant_overrides_spec.rb b/spec/features/consumer/shopping/variant_overrides_spec.rb
index ecb0c4efe8..f4ee8e6275 100644
--- a/spec/features/consumer/shopping/variant_overrides_spec.rb
+++ b/spec/features/consumer/shopping/variant_overrides_spec.rb
@@ -28,7 +28,7 @@ feature "shopping with variant overrides defined", js: true, retry: 3 do
let!(:vo4) { create(:variant_override, hub: hub, variant: v4, count_on_hand: 3, default_stock: nil, resettable: false) }
let!(:vo5) { create(:variant_override, hub: hub, variant: v5, count_on_hand: 0, default_stock: nil, resettable: false) }
let!(:vo6) { create(:variant_override, hub: hub, variant: v6, count_on_hand: 6, default_stock: nil, resettable: false) }
- let(:ef) { create(:enterprise_fee, enterprise: hub, fee_type: 'packing', calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)) }
+ let(:ef) { create(:enterprise_fee, enterprise: hub, fee_type: 'packing', calculator: Calculator::FlatPercentPerItem.new(preferred_flat_percent: 10)) }
before do
outgoing_exchange.variants = [v1, v2, v3, v4, v5, v6]
@@ -91,17 +91,17 @@ feature "shopping with variant overrides defined", js: true, retry: 3 do
page.should have_field "order[line_items_attributes][0][quantity]", with: '2'
page.should have_selector "tr.line-item.variant-#{v1.id} .cart-item-total", text: '$122.22'
- page.should have_selector "#edit-cart .item-total", text: '$122.21'
- page.should have_selector "#edit-cart .grand-total", text: '$122.21'
+ page.should have_selector "#edit-cart .item-total", text: '$122.22'
+ page.should have_selector "#edit-cart .grand-total", text: '$122.22'
end
it "shows the correct prices in the checkout" do
fill_in "variants[#{v1.id}]", with: "2"
click_checkout
- page.should have_selector 'form.edit_order .cart-total', text: '$122.21'
+ page.should have_selector 'form.edit_order .cart-total', text: '$122.22'
page.should have_selector 'form.edit_order .shipping', text: '$0.00'
- page.should have_selector 'form.edit_order .total', text: '$122.21'
+ page.should have_selector 'form.edit_order .total', text: '$122.22'
end
end
@@ -115,7 +115,7 @@ feature "shopping with variant overrides defined", js: true, retry: 3 do
o = Spree::Order.complete.last
o.line_items.first.price.should == 55.55
- o.total.should == 122.21
+ o.total.should == 122.22
end
it "subtracts stock from the override" do
diff --git a/spec/features/consumer/shops_spec.rb b/spec/features/consumer/shops_spec.rb
index adbbf05f9a..2a65a73aef 100644
--- a/spec/features/consumer/shops_spec.rb
+++ b/spec/features/consumer/shops_spec.rb
@@ -56,12 +56,36 @@ feature 'Shops', js: true do
expect(page).to have_current_path enterprise_shop_path(distributor)
end
+ describe "property badges" do
+ let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) }
+ let(:product) { create(:simple_product, supplier: producer) }
+
+ before do
+ product.set_property 'Local', 'XYZ 123'
+ end
+
+ it "shows property badges" do
+ # Given a shop with a product with a property
+ # And the product's producer has a producer property
+
+ # When I go to the shops path
+ visit shops_path
+
+ # And I open the shop
+ expand_active_table_node distributor.name
+
+ # Then I should see both properties
+ expect(page).to have_content 'Local' # Product property
+ expect(page).to have_content 'Organic' # Producer property
+ end
+ end
+
describe "hub producer modal" do
let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) }
let!(:taxon) { create(:taxon, name: 'Fruit') }
let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) }
- it "should show hub producer modals" do
+ it "shows hub producer modals" do
expand_active_table_node distributor.name
expect(page).to have_content producer.name
open_enterprise_modal producer
diff --git a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee
index 5f190b9382..f97ff35d36 100644
--- a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee
+++ b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee
@@ -15,11 +15,15 @@ describe "CustomersCtrl", ->
{ name: "Shop 3", id: 3 }
]
+ availableCountries = [
+ {id: 109, name: "Australia", states: [{id: 55, name: "ACT", abbr: "ACT"}]}
+ ]
+
inject ($controller, $rootScope, _CustomerResource_, $httpBackend) ->
scope = $rootScope
http = $httpBackend
- $controller 'customersCtrl', {$scope: scope, CustomerResource: _CustomerResource_, shops: shops}
+ $controller 'customersCtrl', {$scope: scope, CustomerResource: _CustomerResource_, shops: shops, availableCountries: availableCountries}
jasmine.addMatchers
toDeepEqual: (util, customEqualityTesters) ->
compare: (actual, expected) ->
diff --git a/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb b/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb
index a31295ac9d..0fb42f3052 100644
--- a/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb
+++ b/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb
@@ -66,7 +66,7 @@ module OpenFoodNetwork
end
describe "summing percentage fees for the variant" do
- let!(:enterprise_fee1) { create(:enterprise_fee, amount: 20, fee_type: "admin", calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 20)) }
+ let!(:enterprise_fee1) { create(:enterprise_fee, amount: 20, fee_type: "admin", calculator: ::Calculator::FlatPercentPerItem.new(preferred_flat_percent: 20)) }
let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false,
enterprise_fees: [enterprise_fee1], variants: [product1.master]) }
diff --git a/spec/models/calculator/flat_percent_per_item_spec.rb b/spec/models/calculator/flat_percent_per_item_spec.rb
new file mode 100644
index 0000000000..aaeff2a328
--- /dev/null
+++ b/spec/models/calculator/flat_percent_per_item_spec.rb
@@ -0,0 +1,13 @@
+describe Calculator::FlatPercentPerItem do
+ let(:calculator) { Calculator::FlatPercentPerItem.new preferred_flat_percent: 20 }
+
+ it "calculates for a simple line item" do
+ line_item = Spree::LineItem.new price: 50, quantity: 2
+ expect(calculator.compute(line_item)).to eq 20
+ end
+
+ it "rounds fractional cents before summing" do
+ line_item = Spree::LineItem.new price: 0.86, quantity: 8
+ expect(calculator.compute(line_item)).to eq 1.36
+ end
+end
diff --git a/spec/models/customer_spec.rb b/spec/models/customer_spec.rb
index 32e380f4d3..37134e9574 100644
--- a/spec/models/customer_spec.rb
+++ b/spec/models/customer_spec.rb
@@ -18,6 +18,26 @@ describe Customer, type: :model do
end
end
+ describe 'update shipping address' do
+ let(:customer) { create(:customer) }
+
+ it 'updates the shipping address' do
+ expect(customer.shipping_address).to be_nil
+
+ ship_address = {zipcode: "3127",
+ city: "Melbourne",
+ state_id: 1,
+ phone: "455500146",
+ address1: "U 3/32 Florence Road Surrey Hills2",
+ country_id: 1}
+ customer.update_attributes!(ship_address_attributes: ship_address)
+
+ expect(customer.ship_address.city).to eq 'Melbourne'
+ expect(customer.ship_address.firstname).to eq 'unused'
+ expect(customer.ship_address.lastname).to eq 'unused'
+ end
+ end
+
describe 'creation callbacks' do
let!(:user1) { create(:user) }
let!(:user2) { create(:user) }
diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb
index 7fac566f4b..e0df4ce5d0 100644
--- a/spec/models/enterprise_fee_spec.rb
+++ b/spec/models/enterprise_fee_spec.rb
@@ -75,7 +75,7 @@ describe EnterpriseFee do
it "returns fees with any other calculator" do
ef1 = create(:enterprise_fee, calculator: Spree::Calculator::DefaultTax.new)
- ef2 = create(:enterprise_fee, calculator: Spree::Calculator::FlatPercentItemTotal.new)
+ ef2 = create(:enterprise_fee, calculator: Calculator::FlatPercentPerItem.new)
ef3 = create(:enterprise_fee, calculator: Spree::Calculator::PerItem.new)
ef4 = create(:enterprise_fee, calculator: Spree::Calculator::PriceSack.new)
@@ -93,7 +93,7 @@ describe EnterpriseFee do
it "does not return fees with any other calculator" do
ef1 = create(:enterprise_fee, calculator: Spree::Calculator::DefaultTax.new)
- ef2 = create(:enterprise_fee, calculator: Spree::Calculator::FlatPercentItemTotal.new)
+ ef2 = create(:enterprise_fee, calculator: Calculator::FlatPercentPerItem.new)
ef3 = create(:enterprise_fee, calculator: Spree::Calculator::PerItem.new)
ef4 = create(:enterprise_fee, calculator: Spree::Calculator::PriceSack.new)
diff --git a/spec/models/producer_property_spec.rb b/spec/models/producer_property_spec.rb
index eaa7f0de0c..2d0009ce12 100644
--- a/spec/models/producer_property_spec.rb
+++ b/spec/models/producer_property_spec.rb
@@ -1,14 +1,70 @@
require 'spec_helper'
describe ProducerProperty do
- describe "products caching" do
- let(:producer) { create(:supplier_enterprise) }
- let(:pp) { producer.producer_properties.first }
+ let(:producer) { create(:supplier_enterprise) }
+ let(:pp) { producer.producer_properties.first }
+
+ before do
+ producer.set_producer_property 'Organic Certified', 'NASAA 54321'
+ end
+
+ describe ".sold_by" do
+ let!(:shop) { create(:distributor_enterprise) }
+ let!(:oc) { create(:simple_order_cycle, distributors: [shop], variants: [product.variants.first]) }
+ let(:product) { create(:simple_product, supplier: producer) }
+ let(:producer_other) { create(:supplier_enterprise) }
+ let(:product_other) { create(:simple_product, supplier: producer_other) }
+ let(:pp_other) { producer_other.producer_properties.first }
before do
- producer.set_producer_property 'Organic Certified', 'NASAA 54321'
+ producer_other.set_producer_property 'Spiffy', 'Ya'
end
+ describe "with an associated producer property" do
+ it "returns the producer property" do
+ expect(ProducerProperty.sold_by(shop)).to eq [pp]
+ end
+ end
+
+ describe "with a producer property for a producer not carried by that shop" do
+ let!(:exchange) { create(:exchange, order_cycle: oc, incoming: true, sender: producer_other, receiver: oc.coordinator) }
+
+ it "doesn't return the producer property" do
+ expect(ProducerProperty.sold_by(shop)).not_to include pp_other
+ end
+ end
+
+ describe "with a producer property for a product in a different shop" do
+ let(:shop_other) { create(:distributor_enterprise) }
+ let!(:oc) { create(:simple_order_cycle, distributors: [shop], variants: [product.variants.first]) }
+ let!(:exchange) { create(:exchange, order_cycle: oc, incoming: false, sender: oc.coordinator, receiver: shop_other, variants: [product_other.variants.first]) }
+
+ it "doesn't return the producer property" do
+ expect(ProducerProperty.sold_by(shop)).not_to include pp_other
+ end
+ end
+
+ describe "with a producer property for a product in a closed order cycle" do
+ before do
+ oc.update_attributes! orders_close_at: 1.week.ago
+ end
+
+ it "doesn't return the producer property" do
+ expect(ProducerProperty.sold_by(shop)).not_to include pp
+ end
+ end
+
+ describe "with a duplicate producer property" do
+ let(:product2) { create(:simple_product, supplier: producer) }
+ let!(:oc) { create(:simple_order_cycle, distributors: [shop], variants: [product.variants.first, product2.variants.first]) }
+
+ it "doesn't return duplicates" do
+ expect(ProducerProperty.sold_by(shop).to_a.count).to eq 1
+ end
+ end
+ end
+
+ describe "products caching" do
it "refreshes the products cache on change" do
expect(OpenFoodNetwork::ProductsCache).to receive(:producer_property_changed).with(pp)
pp.value = 123
diff --git a/spec/models/spree/property_spec.rb b/spec/models/spree/property_spec.rb
index 6867b9de1f..6e6ed2d780 100644
--- a/spec/models/spree/property_spec.rb
+++ b/spec/models/spree/property_spec.rb
@@ -30,6 +30,58 @@ module Spree
expect(Spree::Property.applied_by(producer).to_a.count).to eq 1
end
end
+
+ describe ".sold_by" do
+ let!(:shop) { create(:distributor_enterprise) }
+ let!(:shop_other) { create(:distributor_enterprise) }
+ let!(:product) { create(:simple_product) }
+ let!(:product_other_ex) { create(:simple_product) }
+ let!(:product_no_oc) { create(:simple_product) }
+ let!(:product_closed_oc) { create(:simple_product) }
+ let!(:oc) { create(:simple_order_cycle, distributors: [shop], variants: [product.variants.first]) }
+ let!(:oc_closed) { create(:closed_order_cycle, distributors: [shop], variants: [product_closed_oc.variants.first]) }
+ let!(:exchange_other_shop) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: shop_other, variants: [product_other_ex.variants.first]) }
+ let(:property) { product.properties.last }
+ let(:property_other_ex) { product_other_ex.properties.last }
+ let(:property_no_oc) { product_no_oc.properties.last }
+ let(:property_closed_oc) { product_closed_oc.properties.last }
+
+ before do
+ product.set_property 'Organic', 'NASAA 12345'
+ product_other_ex.set_property 'Biodynamic', 'ASDF 12345'
+ product_no_oc.set_property 'Shiny', 'Very'
+ product_closed_oc.set_property 'Spiffy', 'Ooh yeah'
+ end
+
+ it "returns the property" do
+ expect(Property.sold_by(shop)).to eq [property]
+ end
+
+ it "doesn't return the property from another exchange" do
+ expect(Property.sold_by(shop)).not_to include property_other_ex
+ end
+
+ it "doesn't return the property with no order cycle" do
+ expect(Property.sold_by(shop)).not_to include property_no_oc
+ end
+
+ it "doesn't return the property from a closed order cycle" do
+ expect(Property.sold_by(shop)).not_to include property_closed_oc
+ end
+
+ context "with another product in the order cycle" do
+ let!(:product2) { create(:simple_product) }
+ let!(:oc) { create(:simple_order_cycle, distributors: [shop], variants: [product.variants.first, product2.variants.first]) }
+
+ before do
+ product2.set_property 'Organic', 'NASAA 12345'
+ end
+
+ it "doesn't return duplicates" do
+ expect(Property.sold_by(shop).to_a.count).to eq 1
+ end
+ end
+ end
end
describe "callbacks" do
diff --git a/spec/serializers/admin/customer_serializer_spec.rb b/spec/serializers/admin/customer_serializer_spec.rb
index b8e43f5fcc..5694b1004f 100644
--- a/spec/serializers/admin/customer_serializer_spec.rb
+++ b/spec/serializers/admin/customer_serializer_spec.rb
@@ -11,5 +11,9 @@ describe Api::Admin::CustomerSerializer do
expect(tags.length).to eq 3
expect(tags[0]).to eq({ "text" => 'one', "rules" => nil })
expect(tags[1]).to eq({ "text" => 'two', "rules" => 1 })
+
+ expect(result['bill_address']['id']).to eq customer.bill_address.id
+ expect(result['bill_address']['address1']).to eq customer.bill_address.address1
+ expect(result['ship_address']).to be nil
end
end
diff --git a/spec/support/request/shop_workflow.rb b/spec/support/request/shop_workflow.rb
index 01c24bee05..3fc7ac53a6 100644
--- a/spec/support/request/shop_workflow.rb
+++ b/spec/support/request/shop_workflow.rb
@@ -16,9 +16,9 @@ module ShopWorkflow
ApplicationController.any_instance.stub(:session).and_return({order_id: order.id, access_token: order.token})
end
- def add_product_to_cart
+ def add_product_to_cart(order, product, quantity: 1)
populator = Spree::OrderPopulator.new(order, order.currency)
- populator.populate(variants: {product.variants.first.id => 1})
+ populator.populate(variants: {product.variants.first.id => quantity})
# Recalculate fee totals
order.update_distribution_charge!