diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 5f4d64a99f..a4f4eea834 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -2,15 +2,17 @@ module Admin class EnterprisesController < ResourceController before_filter :load_enterprise_set, :only => :index before_filter :load_countries, :except => :index - before_filter :load_methods_and_fees, :only => [:new, :edit] + before_filter :load_methods_and_fees, :only => [:new, :edit, :update, :create] create.after :grant_management helper 'spree/products' + def bulk_update @enterprise_set = EnterpriseSet.new(params[:enterprise_set]) if @enterprise_set.save - redirect_to main_app.admin_enterprises_path, :notice => 'Distributor collection times updated.' + flash[:success] = 'Enterprises updated successfully' + redirect_to main_app.admin_enterprises_path else render :index end @@ -48,5 +50,14 @@ module Admin @shipping_methods = Spree::ShippingMethod.managed_by(spree_current_user).sort_by!{ |sm| [(@enterprise.shipping_methods.include? sm) ? 0 : 1, sm.name] } @enterprise_fees = EnterpriseFee.managed_by(spree_current_user).for_enterprise(@enterprise).order(:fee_type, :name).all end + + # Overriding method on Spree's resource controller + def location_after_save + if params[:enterprise].key? :producer_properties_attributes + main_app.admin_enterprises_path + else + main_app.edit_admin_enterprise_path(@enterprise) + end + end end end diff --git a/app/controllers/admin/producer_properties_controller.rb b/app/controllers/admin/producer_properties_controller.rb new file mode 100644 index 0000000000..b297e74ff2 --- /dev/null +++ b/app/controllers/admin/producer_properties_controller.rb @@ -0,0 +1,26 @@ +module Admin + class ProducerPropertiesController < ResourceController + before_filter :load_enterprise + before_filter :load_properties + before_filter :setup_property, only: [:index] + + + private + + def collection_url + main_app.admin_enterprise_producer_properties_url(@enterprise) + end + + def load_enterprise + @enterprise = Enterprise.find params[:enterprise_id] + end + + def load_properties + @properties = Spree::Property.pluck(:name) + end + + def setup_property + @enterprise.producer_properties.build + end + end +end diff --git a/app/controllers/spree/admin/shipping_methods_controller_decorator.rb b/app/controllers/spree/admin/shipping_methods_controller_decorator.rb index a8f3803085..192d044298 100644 --- a/app/controllers/spree/admin/shipping_methods_controller_decorator.rb +++ b/app/controllers/spree/admin/shipping_methods_controller_decorator.rb @@ -2,6 +2,7 @@ module Spree module Admin ShippingMethodsController.class_eval do before_filter :do_not_destroy_referenced_shipping_methods, :only => :destroy + before_filter :load_hubs, only: [:new, :edit, :create, :update] # Sort shipping methods by distributor name # ! Code copied from Spree::Admin::ResourceController with two added lines @@ -41,6 +42,11 @@ module Spree redirect_to collection_url and return end end + + private + def load_hubs + @hubs = Enterprise.managed_by(spree_current_user).is_distributor.sort_by!{ |d| [(@shipping_method.has_distributor? d) ? 0 : 1, d.name] } + end end end end diff --git a/app/helpers/spree/admin/base_helper_decorator.rb b/app/helpers/spree/admin/base_helper_decorator.rb new file mode 100644 index 0000000000..e278626c5c --- /dev/null +++ b/app/helpers/spree/admin/base_helper_decorator.rb @@ -0,0 +1,20 @@ +module Spree + module Admin + module BaseHelper + # Add url option to pass in link URL + def link_to_remove_fields(name, f, options = {}) + name = '' if options[:no_text] + options[:class] = '' unless options[:class] + options[:class] += 'no-text with-tip' if options[:no_text] + + url = if f.object.persisted? + options[:url] || [:admin, f.object] + else + '#' + end + + link_to_with_icon('icon-trash', name, url, :class => "remove_fields #{options[:class]}", :data => {:action => 'remove'}, :title => t(:remove)) + f.hidden_field(:_destroy) + end + end + end +end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 3c851bde45..cc21f866db 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -4,6 +4,7 @@ class Enterprise < ActiveRecord::Base acts_as_gmappable :process_geocoding => false has_and_belongs_to_many :groups, class_name: 'EnterpriseGroup' + has_many :producer_properties, foreign_key: 'producer_id' has_many :supplied_products, :class_name => 'Spree::Product', :foreign_key => 'supplier_id', :dependent => :destroy has_many :distributed_orders, :class_name => 'Spree::Order', :foreign_key => 'distributor_id' belongs_to :address, :class_name => 'Spree::Address' @@ -19,6 +20,8 @@ class Enterprise < ActiveRecord::Base delegate :latitude, :longitude, :city, :state_name, :to => :address accepts_nested_attributes_for :address + accepts_nested_attributes_for :producer_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? } + has_attached_file :logo, :styles => { :medium => "300x300>", :thumb => "100x100>" } has_attached_file :promo_image, :styles => { :large => "1200x260#", :thumb => "100x100>" } @@ -102,13 +105,6 @@ class Enterprise < ActiveRecord::Base end } - - # Force a distinct count to work around relation count issue https://github.com/rails/rails/issues/5554 - def self.distinct_count - count(distinct: true) - end - - def self.find_near(suburb) enterprises = [] @@ -120,6 +116,20 @@ class Enterprise < ActiveRecord::Base enterprises end + # Force a distinct count to work around relation count issue https://github.com/rails/rails/issues/5554 + def self.distinct_count + count(distinct: true) + end + + def set_producer_property(property_name, property_value) + transaction do + property = Spree::Property.where(name: property_name).first_or_create!(presentation: property_name) + producer_property = ProducerProperty.where(producer_id: id, property_id: property.id).first_or_initialize + producer_property.value = property_value + producer_property.save! + end + end + def has_supplied_products_on_hand? self.supplied_products.where('count_on_hand > 0').present? end diff --git a/app/models/producer_property.rb b/app/models/producer_property.rb new file mode 100644 index 0000000000..178e7646ff --- /dev/null +++ b/app/models/producer_property.rb @@ -0,0 +1,17 @@ +class ProducerProperty < ActiveRecord::Base + belongs_to :property, class_name: 'Spree::Property' + + default_scope order("#{self.table_name}.position") + + + def property_name + property.name if property + end + + def property_name=(name) + unless name.blank? + self.property = Spree::Property.find_by_name(name) || + Spree::Property.create(name: name, presentation: name) + end + end +end diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 44e7d5e02e..e6d93f25aa 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -72,6 +72,8 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit, :update], Exchange can [:admin, :index, :read, :create, :edit, :update], ExchangeFee + can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], ProducerProperty + can [:admin, :index, :create], Enterprise can [:read, :edit, :update, :bulk_update], Enterprise do |enterprise| user.enterprises.include? enterprise diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 37aeb5e3f1..6e116d3e4d 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -95,6 +95,21 @@ Spree::Product.class_eval do # -- Methods + def properties_h + # Product properties override producer properties + ps = supplier.producer_properties.inject(product_properties) do |properties, property| + if properties.find { |p| p.property.presentation == property.property.presentation } + properties + else + properties + [property] + end + end + + ps. + sort_by { |pp| pp.position }. + map { |pp| {presentation: pp.property.presentation, value: pp.value} } + end + def in_distributor?(distributor) self.class.in_distributor(distributor).include? self end diff --git a/app/models/spree/shipping_method_decorator.rb b/app/models/spree/shipping_method_decorator.rb index a9628b3c73..303ab7b96f 100644 --- a/app/models/spree/shipping_method_decorator.rb +++ b/app/models/spree/shipping_method_decorator.rb @@ -1,6 +1,6 @@ Spree::ShippingMethod.class_eval do has_and_belongs_to_many :distributors, join_table: 'distributors_shipping_methods', :class_name => 'Enterprise', association_foreign_key: 'distributor_id' - attr_accessible :distributor_ids + attr_accessible :distributor_ids, :description attr_accessible :require_ship_address scope :managed_by, lambda { |user| @@ -34,6 +34,10 @@ Spree::ShippingMethod.class_eval do end end + def has_distributor?(distributor) + self.distributors.include?(distributor) + end + def adjustment_label 'Shipping' end diff --git a/app/overrides/spree/admin/shipping_methods/_form/add_distributor.html.haml.deface b/app/overrides/spree/admin/shipping_methods/_form/add_distributor.html.haml.deface deleted file mode 100644 index 0604fd996a..0000000000 --- a/app/overrides/spree/admin/shipping_methods/_form/add_distributor.html.haml.deface +++ /dev/null @@ -1,6 +0,0 @@ -/ insert_bottom "[data-hook='admin_shipping_method_form_availability_fields'] > fieldset" - -= f.field_container :distributors do - = f.label :distributor_ids, 'Distributors' - - distributors = Enterprise.is_distributor.managed_by(spree_current_user) | f.object.distributors - = f.collection_select(:distributor_ids, distributors, :id, :name, {include_blank: false}, {class: "select2 fullwidth", multiple: true}) diff --git a/app/overrides/spree/admin/shipping_methods/_form/add_require_ship_address.html.haml.deface b/app/overrides/spree/admin/shipping_methods/_form/add_require_ship_address.html.haml.deface deleted file mode 100644 index b7bffd4c84..0000000000 --- a/app/overrides/spree/admin/shipping_methods/_form/add_require_ship_address.html.haml.deface +++ /dev/null @@ -1,5 +0,0 @@ -/ insert_bottom "[data-hook='admin_shipping_method_form_availability_fields'] > fieldset" - -= f.field_container :shipping_requirements do - = f.label :require_ship_address, "Requires shipping address?" - = f.check_box :require_ship_address diff --git a/app/overrides/spree/admin/shipping_methods/_form/remove_form_availability_fields.deface b/app/overrides/spree/admin/shipping_methods/_form/remove_form_availability_fields.deface new file mode 100644 index 0000000000..7ab45439c4 --- /dev/null +++ b/app/overrides/spree/admin/shipping_methods/_form/remove_form_availability_fields.deface @@ -0,0 +1 @@ +remove "div[data-hook='admin_shipping_method_form_availability_fields']" \ No newline at end of file diff --git a/app/overrides/spree/admin/shipping_methods/_form/remove_form_calculator_fields.deface b/app/overrides/spree/admin/shipping_methods/_form/remove_form_calculator_fields.deface new file mode 100644 index 0000000000..5063888fed --- /dev/null +++ b/app/overrides/spree/admin/shipping_methods/_form/remove_form_calculator_fields.deface @@ -0,0 +1 @@ +remove "div[data-hook='admin_shipping_method_form_calculator_fields']" \ No newline at end of file diff --git a/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface b/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface new file mode 100644 index 0000000000..4dcc351981 --- /dev/null +++ b/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface @@ -0,0 +1,57 @@ +/ replace "div[data-hook='admin_shipping_method_form_fields']" + +.alpha.twelve.columns{"data-hook" => "admin_shipping_method_form_fields"} + .row + .alpha.three.columns + = f.label :name, t(:name) + .omega.eight.columns + = f.text_field :name, :class => 'fullwidth', placeholder: "eg. 'Pick-up from Primary School'" + = error_message_on :shipping_method, :name + .row + .alpha.three.columns + = f.label :description, t(:description) + .omega.eight.columns + = f.text_area :description, class: 'fullwidth', rows: 2, placeholder: "eg. 'Please collect your order from 123 Imaginary St, Northcote, 3070'" + = error_message_on :shipping_method, :description + - if @available_zones.length == 1 + = f.hidden_field :zone_id, value: @available_zones.first.id + - else + .row + .alpha.three.columns + = f.label :zone_id, t(:zone) + .omega.eight.columns + = f.collection_select(:zone_id, @available_zones, :id, :name, {}, {:class => 'select2 fullwidth'}) + = error_message_on :shipping_method, :zone_id + - if spree_current_user.admin? + .row + .alpha.three.columns + = f.label :display_on, t(:display) + .omega.eight.columns + = select(:shipping_method, :display_on, Spree::ShippingMethod::DISPLAY.collect { |display| [t(display), display == :both ? nil : display.to_s] }, {}, {:class => 'select2 fullwidth'}) + = error_message_on :shipping_method, :display_on + .row + -# Shipping Category used to be a select field, but we cut it down to two options to simplify the interface and because we thought 'Collection' and + -# 'Delivery' pretty much covered it. If we need more categories in the future, suggest reimplementing the select box from spree's code. + .three.columns.alpha + %label Category + = f.hidden_field :shipping_category_id, value: nil + - Spree::ShippingCategory.where(name: ['Delivery', 'Collection']).limit(2).each do |shipping_category| + .two.columns + = f.radio_button :shipping_category_id, shipping_category.id +   + = f.label "shipping_category_id_#{shipping_category.id}", t(shipping_category.name) + .row + .alpha.three.columns + = f.label :require_ship_address, "Requires shipping address?" + .two.columns + = f.radio_button :require_ship_address, true +   + = f.label :yes, t(:yes) + .six.columns + = f.radio_button :require_ship_address, false +   + = f.label :no, t(:no) + + .row + .alpha.eleven.columns + = render :partial => 'spree/admin/shared/calculator_fields', :locals => { :f => f } \ No newline at end of file diff --git a/app/overrides/spree/admin/shipping_methods/edit/add_hubs_sidebar.html.haml.deface b/app/overrides/spree/admin/shipping_methods/edit/add_hubs_sidebar.html.haml.deface new file mode 100644 index 0000000000..74fc957e71 --- /dev/null +++ b/app/overrides/spree/admin/shipping_methods/edit/add_hubs_sidebar.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_after "code[erb-loud]:contains(\"render :partial => 'form', :locals => { :f => f }\")" + += render :partial => 'spree/admin/shared/hubs_sidebar', :locals => { :f => f } \ No newline at end of file diff --git a/app/overrides/spree/admin/shipping_methods/edit/remove_configuration_sidebar.deface b/app/overrides/spree/admin/shipping_methods/edit/remove_configuration_sidebar.deface new file mode 100644 index 0000000000..cc3fbcdee1 --- /dev/null +++ b/app/overrides/spree/admin/shipping_methods/edit/remove_configuration_sidebar.deface @@ -0,0 +1 @@ +remove "code[erb-loud]:contains(\"render :partial => 'spree/admin/shared/configuration_menu'\")" \ No newline at end of file diff --git a/app/overrides/spree/admin/shipping_methods/new/add_hubs_sidebar.html.haml.deface b/app/overrides/spree/admin/shipping_methods/new/add_hubs_sidebar.html.haml.deface new file mode 100644 index 0000000000..74fc957e71 --- /dev/null +++ b/app/overrides/spree/admin/shipping_methods/new/add_hubs_sidebar.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_after "code[erb-loud]:contains(\"render :partial => 'form', :locals => { :f => f }\")" + += render :partial => 'spree/admin/shared/hubs_sidebar', :locals => { :f => f } \ No newline at end of file diff --git a/app/overrides/spree/admin/shipping_methods/new/remove_configuration_sidebar.deface b/app/overrides/spree/admin/shipping_methods/new/remove_configuration_sidebar.deface new file mode 100644 index 0000000000..cc3fbcdee1 --- /dev/null +++ b/app/overrides/spree/admin/shipping_methods/new/remove_configuration_sidebar.deface @@ -0,0 +1 @@ +remove "code[erb-loud]:contains(\"render :partial => 'spree/admin/shared/configuration_menu'\")" \ No newline at end of file diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index 9bc0fd0355..d8fab90c6b 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -49,6 +49,15 @@ = f.radio_button :visible, false   = f.label :visible, "Not Visible", :value => "false" + - if @enterprise.is_distributor + - # TODO: Angularise this + .row + .three.columns.alpha + %label Link to shop front + .with-tip{'data-powertip' => "A direct link to your shopfront on the Open Food Network."} + %a What's this? + .eight.columns.omega + = main_app.shop_enterprise_url(@enterprise) = f.fields_for :address do |af| @@ -173,8 +182,10 @@ .row .alpha.three.columns = f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed in "About Us"' - .with-tip{'data-powertip' => 'This image is displayed on the right hand side of the "About Us" section of your public profile.'} - %a What's this? + %br/ + %span{ style: 'font-weight:bold' } PLEASE NOTE: + Any promo image uploaded here will be cropped to 1200 x 260. + This image is displayed on the right hand side of the "About Us" section of your public profile. .omega.eight.columns = image_tag @object.promo_image(:large) if @object.promo_image.present? diff --git a/app/views/admin/enterprises/index.html.erb b/app/views/admin/enterprises/index.html.erb deleted file mode 100644 index 1795b17950..0000000000 --- a/app/views/admin/enterprises/index.html.erb +++ /dev/null @@ -1,76 +0,0 @@ -<% content_for :page_title do %> - Enterprises -<% end %> - -<% content_for :page_actions do %> - -<% end %> - -<%= render 'admin/shared/enterprises_sub_menu' %> - - -<%= form_for @enterprise_set, :url => main_app.bulk_update_admin_enterprises_path do |f| %> - - - - - - - - - - - - - - - - - - - - <%= f.fields_for :collection do |enterprise_form| %> - <% enterprise = enterprise_form.object %> - - - - - - - - <% end %> - <% if @enterprises.empty? %> - - <% end %> - -
NameRoleVisible?Description
<%= link_to enterprise.name, main_app.admin_enterprise_path(enterprise) %><% if enterprise.is_primary_producer && enterprise.is_distributor %> Producer & Distributor - <% elsif enterprise.is_distributor %> Distributor - <% elsif enterprise.is_primary_producer %> Producer - <% else %> -

- <% end %> -
<%= enterprise.visible ? 'Visible' : 'Invisible' %><%= enterprise.description %> - - <%= link_to_with_icon('icon-edit', 'Edit Profile', main_app.edit_admin_enterprise_path(enterprise), class: 'edit') %>
- <%= link_to_delete_enterprise enterprise %>
- - <% if enterprise.is_distributor %> - <%= link_to_with_icon 'icon-chevron-right', 'Payment Methods', spree.admin_payment_methods_path(enterprise_id: enterprise.id) %> (<%= enterprise.payment_methods.count %>) - <% if enterprise.payment_methods.count == 0 %> - - <% end %>
- <%= link_to_with_icon 'icon-plane', 'Shipping Methods', spree.admin_shipping_methods_path(enterprise_id: enterprise.id) %> (<%= enterprise.shipping_methods.count %>) - <% if enterprise.shipping_methods.count == 0 %> - - <% end %>
- <% end %> - <%= link_to_with_icon 'icon-money', 'Enterprise Fees', main_app.admin_enterprise_fees_path(enterprise_id: enterprise.id) %> (<%= enterprise.enterprise_fees.count %>) - <% if enterprise.enterprise_fees.count == 0 %> - - <% end %> -
<%= t(:none) %>
- - <%= f.submit 'Update' %> -<% end %> diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml new file mode 100644 index 0000000000..b358011d89 --- /dev/null +++ b/app/views/admin/enterprises/index.html.haml @@ -0,0 +1,68 @@ +- content_for :page_title do + Enterprises + +- content_for :page_actions do + %li#new_product_link + = button_link_to "New Enterprise", main_app.new_admin_enterprise_path, :icon => 'add', :id => 'admin_new_enterprise_link' + += render 'admin/shared/enterprises_sub_menu' + += form_for @enterprise_set, :url => main_app.bulk_update_admin_enterprises_path do |f| + %table#listing_enterprises.index + %colgroup + %col{style: "width: 20%;"}/ + %col{style: "width: 10%;"}/ + %col{style: "width: 5%;"}/ + %col/ + %col{style: "width: 20%;"}/ + %thead + %tr{"data-hook" => "enterprises_header"} + %th Name + %th Role + %th Visible? + %th Description + %th + %tbody + = f.fields_for :collection do |enterprise_form| + - enterprise = enterprise_form.object + %tr{class: "enterprise-#{enterprise.id}"} + %td= link_to enterprise.name, main_app.admin_enterprise_path(enterprise) + %td + - if enterprise.is_primary_producer && enterprise.is_distributor + Producer & Distributor + - elsif enterprise.is_distributor + Distributor + - elsif enterprise.is_primary_producer + Producer + - else + %h1.icon-exclamation-sign.with-tip{"data-powertip" => "This enterprise does not have any roles", style: "text-align: center;color: #DA5354"} + %td= enterprise_form.check_box :visible + %td= enterprise.description + %td{"data-hook" => "admin_users_index_row_actions"} + = link_to_with_icon('icon-edit', 'Edit Profile', main_app.edit_admin_enterprise_path(enterprise), class: 'edit') + %br/ + = link_to_delete_enterprise enterprise + %br/ + - if enterprise.is_primary_producer + = link_to_with_icon 'icon-dashboard', 'Properties', main_app.admin_enterprise_producer_properties_path(enterprise_id: enterprise.id) + (#{enterprise.producer_properties.count}) + %br/ + - if enterprise.is_distributor + = link_to_with_icon 'icon-chevron-right', 'Payment Methods', spree.admin_payment_methods_path(enterprise_id: enterprise.id) + (#{enterprise.payment_methods.count}) + - if enterprise.payment_methods.count == 0 + %span.icon-exclamation-sign.with-tip{"data-powertip" => "This enterprise has no payment methods", style: "font-size: 16px;color: #DA5354"} + %br/ + = link_to_with_icon 'icon-plane', 'Shipping Methods', spree.admin_shipping_methods_path(enterprise_id: enterprise.id) + (#{enterprise.shipping_methods.count}) + - if enterprise.shipping_methods.count == 0 + %span.icon-exclamation-sign.with-tip{"data-powertip" => "This enterprise has shipping methods", style: "font-size: 16px;color: #DA5354"} + %br/ + = link_to_with_icon 'icon-money', 'Enterprise Fees', main_app.admin_enterprise_fees_path(enterprise_id: enterprise.id) + (#{enterprise.enterprise_fees.count}) + - if enterprise.enterprise_fees.count == 0 + %span.icon-warning-sign.with-tip{"data-powertip" => "This enterprise has no fees", style: "font-size: 16px;color: orange"} + - if @enterprises.empty? + %tr + %td{colspan: "4"}= t(:none) + = f.submit 'Update' diff --git a/app/views/admin/producer_properties/_producer_property_fields.html.haml b/app/views/admin/producer_properties/_producer_property_fields.html.haml new file mode 100644 index 0000000000..79dc815f9d --- /dev/null +++ b/app/views/admin/producer_properties/_producer_property_fields.html.haml @@ -0,0 +1,12 @@ +-# admin/admin.js.erb in spree requires id to start with "spree_" for sortable tables +%tr.product_property.fields{"data-hook" => "producer_property", id: "spree_#{dom_id(f.object)}"} + %td.no-border + %span.handle + = f.hidden_field :id + %td.property_name + = f.text_field :property_name, :class => 'autocomplete' + %td.value + = f.text_field :value, :class => 'autocomplete' + %td.actions + - unless @enterprise.producer_properties.empty? + = link_to_remove_fields t(:remove), f, no_text: true, url: (f.object.persisted? && main_app.admin_enterprise_producer_property_path(@enterprise, f.object)) diff --git a/app/views/admin/producer_properties/index.html.haml b/app/views/admin/producer_properties/index.html.haml new file mode 100644 index 0000000000..2b4277b08d --- /dev/null +++ b/app/views/admin/producer_properties/index.html.haml @@ -0,0 +1,40 @@ +- content_for :page_title do + = "#{@enterprise.name}:" + Producer Properties + + +- content_for :page_actions do + %ul.tollbar.inline-menu + %li + = link_to_add_fields 'Add Producer Property', 'tbody#producer_properties', class: 'icon-plus button' + + += render 'spree/shared/error_messages', target: @enterprise + + += form_for @enterprise, url: main_app.admin_enterprise_path(@enterprise), method: :put do |f| + %fieldset.no-border-top + .add_producer_properties{"data-hook" => "add_producer_properties"} + = image_tag 'spinner.gif', :plugin => 'spree', :style => 'display:none;', :id => 'busy_indicator' + %table.index.sortable{"data-hook" => "", "data-sortable-link" => main_app.update_positions_admin_enterprise_producer_properties_url(@enterprise)} + %thead + %tr{"data-hook" => "producer_properties_header"} + %th{colspan: "2"} Property + %th Value + %th.actions + %tbody#producer_properties{"data-hook" => ""} + = f.fields_for :producer_properties do |pp_form| + = render 'producer_property_fields', f: pp_form + = render 'spree/admin/shared/edit_resource_links', collection_url: main_app.admin_enterprise_producer_properties_path(@enterprise) + = hidden_field_tag 'clear_producer_properties', 'true' + +:javascript + var properties = #{raw(@properties.to_json)}; + + $("#producer_properties input.autocomplete").live("keydown", function() { + already_auto_completed = $(this).is('ac_input'); + if (!already_auto_completed) { + $(this).autocomplete({source: properties}); + $(this).focus(); + } + }); diff --git a/app/views/spree/admin/shared/_hubs_sidebar.html.haml b/app/views/spree/admin/shared/_hubs_sidebar.html.haml new file mode 100644 index 0000000000..8998e6d593 --- /dev/null +++ b/app/views/spree/admin/shared/_hubs_sidebar.html.haml @@ -0,0 +1,21 @@ +- hubs_color = @hubs.count > 0 ? "blue" : "red" +.sidebar_item.omega.four.columns#hubs + .four.columns.alpha.header{ class: "#{hubs_color}" } + %span.four.columns.alpha.centered Distributors + .four.columns.alpha.list{ class: "#{hubs_color}" } + - if @hubs.count > 0 + -# = hidden_field_tag "enterprise[hub_ids][]", [] + - @hubs.each do |hub| + %a.four.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{main_app.edit_admin_enterprise_path(hub)}" } + %span.three.columns.alpha + = hub.name + %span.one.column.omega + = f.check_box :distributor_ids, { multiple: true }, hub.id, nil + - else + .four.columns.alpha.list-item + %span.three.columns.alpha None Available + %span.one.column.omega + %span.icon-remove-sign + %a.four.columns.alpha.button{ href: "#{main_app.admin_enterprises_path}", class: "#{hubs_color}" } + MANAGE + %span.icon-arrow-right \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 0622acb050..b151e4bae8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,6 +45,10 @@ Openfoodnetwork::Application.routes.draw do resources :enterprises do post :bulk_update, :on => :collection, :as => :bulk_update + + resources :producer_properties do + post :update_positions, on: :collection + end end resources :enterprise_relationships diff --git a/db/migrate/20140613004344_create_producer_properties.rb b/db/migrate/20140613004344_create_producer_properties.rb new file mode 100644 index 0000000000..b45b3bed5d --- /dev/null +++ b/db/migrate/20140613004344_create_producer_properties.rb @@ -0,0 +1,18 @@ +class CreateProducerProperties < ActiveRecord::Migration + def change + create_table :producer_properties do |t| + t.string :value + t.references :producer + t.references :property + t.integer :position + t.timestamps + end + + add_index :producer_properties, :producer_id + add_index :producer_properties, :property_id + add_index :producer_properties, :position + + add_foreign_key :producer_properties, :enterprises, column: :producer_id + add_foreign_key :producer_properties, :spree_properties, column: :property_id + end +end diff --git a/db/schema.rb b/db/schema.rb index a40a1414e1..5932749993 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 => 20140612020206) do +ActiveRecord::Schema.define(:version => 20140613004344) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -312,6 +312,19 @@ ActiveRecord::Schema.define(:version => 20140612020206) do t.datetime "updated_at", :null => false end + create_table "producer_properties", :force => true do |t| + t.string "value" + t.integer "producer_id" + t.integer "property_id" + t.integer "position" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "producer_properties", ["position"], :name => "index_producer_properties_on_position" + add_index "producer_properties", ["producer_id"], :name => "index_producer_properties_on_producer_id" + add_index "producer_properties", ["property_id"], :name => "index_producer_properties_on_property_id" + create_table "product_distributions", :force => true do |t| t.integer "product_id" t.integer "distributor_id" @@ -1052,6 +1065,9 @@ ActiveRecord::Schema.define(:version => 20140612020206) do add_foreign_key "order_cycles", "enterprises", name: "order_cycles_coordinator_id_fk", column: "coordinator_id" + add_foreign_key "producer_properties", "enterprises", name: "producer_properties_producer_id_fk", column: "producer_id" + add_foreign_key "producer_properties", "spree_properties", name: "producer_properties_property_id_fk", column: "property_id" + add_foreign_key "product_distributions", "enterprise_fees", name: "product_distributions_enterprise_fee_id_fk" add_foreign_key "product_distributions", "enterprises", name: "product_distributions_distributor_id_fk", column: "distributor_id" add_foreign_key "product_distributions", "spree_products", name: "product_distributions_product_id_fk", column: "product_id" diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 765a6251bf..50ce2aad7f 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -33,6 +33,23 @@ feature %q{ end end + scenario "editing enterprises in bulk" do + s = create(:supplier_enterprise) + d = create(:distributor_enterprise) + + login_to_admin_section + click_link 'Enterprises' + + within("tr.enterprise-#{d.id}") do + page.should have_checked_field "enterprise_set_collection_attributes_0_visible" + uncheck "enterprise_set_collection_attributes_0_visible" + end + click_button "Update" + flash_message.should == 'Enterprises updated successfully' + distributor = Enterprise.find(d.id) + distributor.visible.should == false + end + scenario "viewing an enterprise" do e = create(:enterprise) @@ -136,16 +153,82 @@ feature %q{ click_button 'Update' flash_message.should == 'Enterprise "Eaterprises" has been successfully updated!' - page.should have_selector '#listing_enterprises a', text: 'Eaterprises' - - click_link 'Edit Profile' + page.should have_field 'enterprise_name', :with => 'Eaterprises' page.should have_checked_field "enterprise_payment_method_ids_#{payment_method.id}" page.should have_checked_field "enterprise_shipping_method_ids_#{shipping_method.id}" page.should have_selector "a.list-item", text: enterprise_fee.name end - context 'as an Enterprise user' do + describe "producer properties" do + it "creates producer properties" do + # Given a producer enterprise + s = create(:supplier_enterprise) + + # When I go to its properties page + login_to_admin_section + click_link 'Enterprises' + within(".enterprise-#{s.id}") { click_link 'Properties' } + + # And I create a property + fill_in 'enterprise_producer_properties_attributes_0_property_name', with: "Certified Organic" + fill_in 'enterprise_producer_properties_attributes_0_value', with: "NASAA 12345" + click_button 'Update' + + # Then I should be returned to the enterprises page + page.should have_selector '#listing_enterprises a', text: s.name + + # And the producer should have the property + s.producer_properties(true).count.should == 1 + s.producer_properties.first.property.presentation.should == "Certified Organic" + s.producer_properties.first.value.should == "NASAA 12345" + end + + it "updates producer properties" do + # Given a producer enterprise with a property + s = create(:supplier_enterprise) + s.producer_properties.create! property_name: 'Certified Organic', value: 'NASAA 12345' + + # When I go to its properties page + login_to_admin_section + visit main_app.admin_enterprise_producer_properties_path(s) + + # And I update the property + fill_in 'enterprise_producer_properties_attributes_0_property_name', with: "Biodynamic" + fill_in 'enterprise_producer_properties_attributes_0_value', with: "Shininess" + click_button 'Update' + + # Then I should be returned to the enterprises + page.should have_selector '#listing_enterprises a', text: s.name + + # And the property should be updated + s.producer_properties(true).count.should == 1 + s.producer_properties.first.property.presentation.should == "Biodynamic" + s.producer_properties.first.value.should == "Shininess" + end + + it "removes producer properties", js: true do + # Given a producer enterprise with a property + s = create(:supplier_enterprise) + pp = s.producer_properties.create! property_name: 'Certified Organic', value: 'NASAA 12345' + + # When I go to its properties page + login_to_admin_section + visit main_app.admin_enterprise_producer_properties_path(s) + + # And I remove the property + page.should have_field 'enterprise_producer_properties_attributes_0_property_name', with: 'Certified Organic' + within("#spree_producer_property_#{pp.id}") { page.find('a.remove_fields').click } + + # Then the property should have been removed + page.should_not have_selector '#progress' + page.should_not have_field 'enterprise_producer_properties_attributes_0_property_name', with: 'Certified Organic' + s.producer_properties(true).should be_empty + end + end + + + context "as an Enterprise user" do let(:supplier1) { create(:supplier_enterprise, name: 'First Supplier') } let(:supplier2) { create(:supplier_enterprise, name: 'Another Supplier') } let(:distributor1) { create(:distributor_enterprise, name: 'First Distributor') } @@ -190,7 +273,7 @@ feature %q{ Enterprise.managed_by(@new_user).should include enterprise end - scenario "can edit enterprises I have permission to" do + scenario "editing enterprises I have permission to" do click_link 'Enterprises' within('#listing_enterprises tbody tr:first') { click_link 'Edit Profile' } @@ -198,14 +281,34 @@ feature %q{ click_button 'Update' flash_message.should == 'Enterprise "Eaterprises" has been successfully updated!' - page.should have_selector '#listing_enterprises a', text: 'Eaterprises' + page.should have_field 'enterprise_name', :with => 'Eaterprises' end - scenario "Editing images for an enterprise" do + scenario "editing images for an enterprise" do click_link 'Enterprises' first(".edit").click page.should have_content "Logo" page.should have_content "Promo" end + + scenario "managing producer properties", js: true do + click_link 'Enterprises' + within(".enterprise-#{supplier1.id}") { click_link 'Properties' } + + # -- Create / update + fill_in 'enterprise_producer_properties_attributes_0_property_name', with: "Certified Organic" + fill_in 'enterprise_producer_properties_attributes_0_value', with: "NASAA 12345" + click_button 'Update' + page.should have_selector '#listing_enterprises a', text: supplier1.name + supplier1.producer_properties(true).count.should == 1 + + # -- Destroy + pp = supplier1.producer_properties.first + within(".enterprise-#{supplier1.id}") { click_link 'Properties' } + + within("#spree_producer_property_#{pp.id}") { page.find('a.remove_fields').click } + page.should_not have_selector '#progress' + supplier1.producer_properties(true).should be_empty + end end end diff --git a/spec/features/admin/shipping_methods_spec.rb b/spec/features/admin/shipping_methods_spec.rb index ab7940acc2..94efc2aacf 100644 --- a/spec/features/admin/shipping_methods_spec.rb +++ b/spec/features/admin/shipping_methods_spec.rb @@ -17,12 +17,22 @@ feature 'shipping methods' do # Given some distributors d1 = create(:distributor_enterprise, name: 'Aeronautical Adventures') d2 = create(:distributor_enterprise, name: 'Nautical Travels') + sc1 = create(:shipping_category, name: 'Delivery') + sc2 = create(:shipping_category, name: 'Collection') + + # Shows appropriate fields when logged in as admin + visit spree.new_admin_shipping_method_path + page.should have_field 'shipping_method_name' + page.should have_field 'shipping_method_description' + page.should have_select 'shipping_method_display_on' + page.should have_field "shipping_method_shipping_category_id_#{sc1.id}" + page.should have_field "shipping_method_shipping_category_id_#{sc2.id}" + page.should have_field 'shipping_method_require_ship_address_true', checked: true # When I create a shipping method and set the distributors - visit spree.new_admin_shipping_method_path fill_in 'shipping_method_name', with: 'Carrier Pidgeon' - select 'Aeronautical Adventures', from: 'shipping_method_distributor_ids' - select 'Nautical Travels', from: 'shipping_method_distributor_ids' + check "shipping_method_distributor_ids_#{d1.id}" + check "shipping_method_distributor_ids_#{d2.id}" click_button 'Create' # Then the shipping method should have its distributor set @@ -70,22 +80,24 @@ feature 'shipping methods' do login_to_admin_as enterprise_user end - it "lets me choose whether a shipping address is required" do - click_link "Enterprises" - within(".enterprise-#{distributor1.id}") { click_link 'Shipping Methods' } - click_link 'New Shipping Method' - - page.should have_content "Requires shipping address?" - end - - it "creates shipping methods" do + it "creating a shipping method" do + sc1 = create(:shipping_category, name: 'Delivery') + sc2 = create(:shipping_category, name: 'Collection') click_link 'Enterprises' within(".enterprise-#{distributor1.id}") { click_link 'Shipping Methods' } click_link 'New Shipping Method' + # Show the correct fields + page.should have_field 'shipping_method_name' + page.should have_field 'shipping_method_description' + page.should_not have_select 'shipping_method_display_on' + page.should have_field "shipping_method_shipping_category_id_#{sc1.id}" + page.should have_field "shipping_method_shipping_category_id_#{sc2.id}" + page.should have_field 'shipping_method_require_ship_address_true', checked: true + fill_in 'shipping_method_name', :with => 'Teleport' - select distributor1.name, :from => 'shipping_method_distributor_ids' + check "shipping_method_distributor_ids_#{distributor1.id}" click_button 'Create' flash_message.should == 'Shipping method "Teleport" has been successfully created!' diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 542d95a4b0..bdf5574baf 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -449,14 +449,27 @@ describe Enterprise do describe "presentation of attributes" do let(:distributor) { create(:distributor_enterprise, - website: "http://www.google.com", - facebook: "www.facebook.com/roger", - linkedin: "http://linkedin.com") + website: "http://www.google.com", + facebook: "www.facebook.com/roger", + linkedin: "https://linkedin.com") } + it "strips http and www from url fields" do distributor.website.should == "google.com" distributor.facebook.should == "facebook.com/roger" distributor.linkedin.should == "linkedin.com" end end + + describe "producer properties" do + let(:supplier) { create(:supplier_enterprise) } + + it "sets producer properties" do + supplier.set_producer_property 'Organic Certified', 'NASAA 12345' + + supplier.producer_properties.count.should == 1 + supplier.producer_properties.first.value.should == 'NASAA 12345' + supplier.producer_properties.first.property.presentation.should == 'Organic Certified' + end + end end diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 111c8e5196..9705fee70e 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -75,6 +75,10 @@ module Spree should have_ability([:admin, :index, :read, :create, :edit], for: Spree::Classification) end + it "should be able to read/write their enterprises' producer properties" do + should have_ability([:admin, :index, :read, :create, :edit, :update_positions, :destroy], for: ProducerProperty) + end + it "should be able to read and create enterprise relationships" do should have_ability([:admin, :index, :create], for: EnterpriseRelationship) end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 594dfa5d29..bba8a95301 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -290,6 +290,52 @@ module Spree end end + describe "properties" do + it "returns product properties as a hash" do + product = create(:simple_product) + product.set_property 'Organic Certified', 'NASAA 12345' + + product.properties_h.should == [{presentation: 'Organic Certified', value: 'NASAA 12345'}] + end + + it "returns producer properties as a hash" do + supplier = create(:supplier_enterprise) + product = create(:simple_product, supplier: supplier) + + supplier.set_producer_property 'Organic Certified', 'NASAA 54321' + + product.properties_h.should == [{presentation: 'Organic Certified', value: 'NASAA 54321'}] + end + + it "overrides producer properties with product properties" do + supplier = create(:supplier_enterprise) + product = create(:simple_product, supplier: supplier) + + product.set_property 'Organic Certified', 'NASAA 12345' + supplier.set_producer_property 'Organic Certified', 'NASAA 54321' + + product.properties_h.should == [{presentation: 'Organic Certified', value: 'NASAA 12345'}] + end + + it "sorts by position" do + supplier = create(:supplier_enterprise) + product = create(:simple_product, supplier: supplier) + + pa = Spree::Property.create! name: 'A', presentation: 'A' + pb = Spree::Property.create! name: 'B', presentation: 'B' + pc = Spree::Property.create! name: 'C', presentation: 'C' + + product.product_properties.create!({property_id: pa.id, value: '1', position: 1}, {without_protection: true}) + product.product_properties.create!({property_id: pc.id, value: '3', position: 3}, {without_protection: true}) + supplier.producer_properties.create!({property_id: pb.id, value: '2', position: 2}, {without_protection: true}) + + product.properties_h.should == + [{presentation: 'A', value: '1'}, + {presentation: 'B', value: '2'}, + {presentation: 'C', value: '3'}] + end + end + describe "membership" do it "queries its membership of a particular product distribution" do d1 = create(:distributor_enterprise)