mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Auto-merge from CI [skip ci]
This commit is contained in:
@@ -1,26 +1,18 @@
|
||||
.row
|
||||
.small-12.large-8.columns
|
||||
/ TODO: Rob add logic for taxons and properties too:
|
||||
/ %div{"ng-if" => "enterprise.long_description.length > 0 || enterprise.logo"}
|
||||
%div
|
||||
%p.modal-header {{'label_about' | t}}
|
||||
/ TODO: Rob - add in taxons and properties and property pop-overs
|
||||
%div
|
||||
%span.filter-shopfront.taxon-selectors
|
||||
%ul.inline-block
|
||||
%li{"ng-repeat" => "taxon in enterprise.supplied_taxons"}
|
||||
%a.button.tiny.disabled{"ng-bind" => "taxon.name"}
|
||||
|
||||
%span.filter-shopfront.property-selectors.pad-top
|
||||
%ul.inline-block
|
||||
%li{"ng-repeat" => "property in enterprise.properties"}
|
||||
%a.button.tiny{"ng-bind" => "property.presentation"}
|
||||
|
||||
-# TODO: Add producer taxons and properties here
|
||||
-# %div
|
||||
-# %span.filter-shopfront.taxon-selectors
|
||||
-# %ul.inline-block
|
||||
-# %li
|
||||
-# %a.button.tiny.disabled Grains
|
||||
-# %li
|
||||
-# %a.button.tiny.disabled Dairy
|
||||
-#
|
||||
-# %span.filter-shopfront.property-selectors.pad-top
|
||||
-# %ul.inline-block
|
||||
-# %li
|
||||
-# %a.button.tiny Organic certified
|
||||
-# / TODO: Rob - need popover, use will's directive or this? http://pineconellc.github.io/angular-foundation/
|
||||
-#
|
||||
.about-container.pad-top
|
||||
%img.enterprise-logo{"ng-src" => "{{::enterprise.logo}}", "ng-if" => "::enterprise.logo"}
|
||||
%p.text-small{"ng-bind-html" => "::enterprise.long_description"}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
.active_table .active_table_node
|
||||
|
||||
// Header row
|
||||
@media all and (max-width: 640px)
|
||||
@media all and (max-width: 640px)
|
||||
.skinny-head
|
||||
background-color: $clr-turquoise-light
|
||||
@include border-radius-mixed(0.5em, 0.5em, 0, 0)
|
||||
@@ -16,7 +16,7 @@
|
||||
.follow-icons
|
||||
&, & *
|
||||
font-size: 1.5rem
|
||||
|
||||
|
||||
|
||||
// Producer icons
|
||||
i.ofn-i_059-producer, i.ofn-i_060-producer-reversed
|
||||
@@ -41,11 +41,11 @@
|
||||
&:hover, &:focus, &:active
|
||||
&.secondary
|
||||
color: #666
|
||||
.hub-name, .button-address
|
||||
.hub-name, .button-address
|
||||
border-bottom: 1px solid #999
|
||||
&.primary
|
||||
color: $clr-brick-bright
|
||||
.hub-name, .button-address
|
||||
.hub-name, .button-address
|
||||
border-bottom: 1px solid $clr-brick-bright
|
||||
|
||||
p.word-wrap
|
||||
@@ -56,6 +56,10 @@
|
||||
.fat-taxons
|
||||
background-color: $clr-turquoise-light
|
||||
|
||||
.fat-properties
|
||||
background-color: $clr-turquoise-ultra-light
|
||||
border: 1px solid $clr-turquoise-light
|
||||
|
||||
.producer-name
|
||||
color: $clr-turquoise
|
||||
|
||||
@@ -72,7 +76,7 @@
|
||||
max-height: 160px
|
||||
width: auto
|
||||
&.left
|
||||
padding: 0.25rem 1rem 0.25rem 0
|
||||
padding: 0.25rem 1rem 0.25rem 0
|
||||
&.right
|
||||
padding: 0.25rem 0.5rem 0.25rem 2rem
|
||||
|
||||
@@ -87,10 +91,7 @@
|
||||
&.closed
|
||||
.active_table_row.closed
|
||||
border: 1px solid transparent
|
||||
@media all and (max-width: 640px)
|
||||
@media all and (max-width: 640px)
|
||||
border-color: $clr-turquoise-light
|
||||
&:hover, &:active, &:focus
|
||||
border-color: $clr-turquoise
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import branding
|
||||
@import mixins
|
||||
|
||||
.fat-taxons
|
||||
.fat-taxons, .fat-properties
|
||||
display: inline-block
|
||||
line-height: 1
|
||||
margin-right: 0.5rem
|
||||
@@ -29,7 +29,7 @@
|
||||
svg
|
||||
path
|
||||
fill: $disabled-dark
|
||||
|
||||
|
||||
.product-header
|
||||
render-svg
|
||||
svg
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require 'open_food_network/permalink_generator'
|
||||
require 'open_food_network/property_merge'
|
||||
|
||||
Spree::Product.class_eval do
|
||||
include PermalinkGenerator
|
||||
@@ -128,11 +129,7 @@ Spree::Product.class_eval do
|
||||
ps = product_properties.all
|
||||
|
||||
if inherits_properties
|
||||
supplier.producer_properties.each do |producer_property|
|
||||
unless ps.find { |product_property| product_property.property.presentation == producer_property.property.presentation }
|
||||
ps << producer_property
|
||||
end
|
||||
end
|
||||
ps = OpenFoodNetwork::PropertyMerge.merge(ps, supplier.producer_properties)
|
||||
end
|
||||
|
||||
ps.
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
module Spree
|
||||
Property.class_eval do
|
||||
scope :applied_by, ->(enterprise) {
|
||||
select('DISTINCT spree_properties.*').
|
||||
joins(:product_properties).
|
||||
where('spree_product_properties.product_id IN (?)', enterprise.supplied_product_ids)
|
||||
}
|
||||
|
||||
after_save :refresh_products_cache
|
||||
|
||||
# When a Property is destroyed, dependent-destroy will destroy all ProductProperties,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'open_food_network/property_merge'
|
||||
|
||||
class Api::EnterpriseSerializer < ActiveModel::Serializer
|
||||
# We reference this here because otherwise the serializer complains about its absence
|
||||
Api::IdSerializer
|
||||
@@ -47,7 +49,7 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :taxons, :supplied_taxons
|
||||
|
||||
has_one :address, serializer: Api::AddressSerializer
|
||||
|
||||
has_many :properties, serializer: Api::PropertySerializer
|
||||
|
||||
def taxons
|
||||
ids_to_objs options[:data].distributed_taxons[object.id]
|
||||
@@ -57,6 +59,14 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer
|
||||
ids_to_objs options[:data].supplied_taxons[object.id]
|
||||
end
|
||||
|
||||
def 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 pickup
|
||||
services = options[:data].shipping_method_services[object.id]
|
||||
services ? services[:pickup] : false
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
class Api::PropertySerializer < ActiveModel::Serializer
|
||||
|
||||
attributes :id, :name, :presentation
|
||||
end
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
%label
|
||||
= t :producers_buy
|
||||
%p.trans-sentence
|
||||
%span.fat-taxons{"ng-repeat" => "taxon in producer.supplied_taxons"}
|
||||
%render-svg{path: "{{taxon.icon}}"}
|
||||
%span{"ng-bind" => "::taxon.name"}
|
||||
%div
|
||||
%span.fat-taxons{"ng-repeat" => "taxon in producer.supplied_taxons"}
|
||||
%render-svg{path: "{{taxon.icon}}"}
|
||||
%span{"ng-bind" => "::taxon.name"}
|
||||
%div
|
||||
%span.fat-properties{"ng-repeat" => "property in producer.properties"}
|
||||
%span{"ng-bind" => "property.presentation"}
|
||||
|
||||
%div.show-for-medium-up{"ng-if" => "producer.supplied_taxons.length==0"}
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ module OpenFoodNetwork
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.all? { |li| li.order.paid? } ? "Yes" : "No" },
|
||||
|
||||
proc { |line_items| line_items.first.order.shipping_method.andand.name },
|
||||
proc { |line_items| rsa.call(line_items) ? 'Y' : 'N' },
|
||||
|
||||
18
lib/open_food_network/property_merge.rb
Normal file
18
lib/open_food_network/property_merge.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
module OpenFoodNetwork
|
||||
class PropertyMerge
|
||||
def self.merge(primary, secondary)
|
||||
primary + secondary.reject do |secondary_p|
|
||||
primary.any? do |primary_p|
|
||||
property_of(primary_p).presentation == property_of(secondary_p).presentation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def self.property_of(p)
|
||||
p.respond_to?(:property) ? p.property : p
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -259,6 +259,12 @@ FactoryGirl.define do
|
||||
end
|
||||
end
|
||||
|
||||
factory :producer_property, class: ProducerProperty do
|
||||
value 'abc123'
|
||||
producer { create(:supplier_enterprise) }
|
||||
property
|
||||
end
|
||||
|
||||
factory :customer, :class => Customer do
|
||||
email { Faker::Internet.email }
|
||||
enterprise
|
||||
|
||||
@@ -5,22 +5,58 @@ feature %q{
|
||||
I want to see a list of producers
|
||||
So that I can shop at hubs distributing their products
|
||||
}, js: true do
|
||||
include WebHelper
|
||||
include UIComponentHelper
|
||||
let!(:producer) { create(:supplier_enterprise) }
|
||||
|
||||
let!(:producer1) { create(:supplier_enterprise) }
|
||||
let!(:producer2) { create(:supplier_enterprise) }
|
||||
let!(:invisible_producer) { create(:supplier_enterprise, visible: false) }
|
||||
let(:taxon) { create(:taxon) }
|
||||
let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) }
|
||||
|
||||
let(:taxon_fruit) { create(:taxon, name: 'Fruit') }
|
||||
let(:taxon_veg) { create(:taxon, name: 'Vegetables') }
|
||||
|
||||
let!(:product1) { create(:simple_product, supplier: producer1, taxons: [taxon_fruit]) }
|
||||
let!(:product2) { create(:simple_product, supplier: producer2, taxons: [taxon_veg]) }
|
||||
|
||||
let(:shop) { create(:distributor_enterprise) }
|
||||
let!(:er) { create(:enterprise_relationship, parent: shop, child: producer) }
|
||||
let!(:er) { create(:enterprise_relationship, parent: shop, child: producer1) }
|
||||
|
||||
before do
|
||||
product1.set_property 'Organic', 'NASAA 12345'
|
||||
product2.set_property 'Biodynamic', 'ABC123'
|
||||
|
||||
producer1.set_producer_property 'Local', 'Victoria'
|
||||
producer2.set_producer_property 'Fair Trade', 'FT123'
|
||||
|
||||
visit producers_path
|
||||
end
|
||||
|
||||
|
||||
it "filters by taxon" do
|
||||
toggle_filters
|
||||
|
||||
toggle_filter 'Vegetables'
|
||||
|
||||
page.should_not have_content producer1.name
|
||||
page.should have_content producer2.name
|
||||
|
||||
toggle_filter 'Vegetables'
|
||||
toggle_filter 'Fruit'
|
||||
|
||||
page.should have_content producer1.name
|
||||
page.should_not have_content producer2.name
|
||||
end
|
||||
|
||||
it "shows all producers with expandable details" do
|
||||
page.should have_content producer.name
|
||||
expand_active_table_node producer.name
|
||||
page.should have_content producer.supplied_taxons.first.name.split.map(&:capitalize).join(' ')
|
||||
page.should have_content producer1.name
|
||||
expand_active_table_node producer1.name
|
||||
|
||||
# -- Taxons
|
||||
page.should have_content 'Fruit'
|
||||
|
||||
# -- Properties
|
||||
page.should have_content 'Organic' # Product property
|
||||
page.should have_content 'Local' # Producer property
|
||||
end
|
||||
|
||||
it "doesn't show invisible producers" do
|
||||
@@ -28,7 +64,19 @@ feature %q{
|
||||
end
|
||||
|
||||
it "links to places to buy produce" do
|
||||
expand_active_table_node producer.name
|
||||
expand_active_table_node producer1.name
|
||||
page.should have_link shop.name
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def toggle_filters
|
||||
find('a.filterbtn').click
|
||||
end
|
||||
|
||||
def toggle_filter(name)
|
||||
page.find('span', text: name).click
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -13,6 +13,7 @@ feature 'Shops', js: true do
|
||||
let!(:er) { create(:enterprise_relationship, parent: distributor, child: producer) }
|
||||
|
||||
before do
|
||||
producer.set_producer_property 'Organic', 'NASAA 12345'
|
||||
visit shops_path
|
||||
end
|
||||
|
||||
@@ -45,11 +46,22 @@ feature 'Shops', js: true do
|
||||
expect(page).to have_current_path enterprise_shop_path(distributor)
|
||||
end
|
||||
|
||||
it "should show hub producer modals" do
|
||||
expand_active_table_node distributor.name
|
||||
expect(page).to have_content producer.name
|
||||
open_enterprise_modal producer
|
||||
modal_should_be_open_for producer
|
||||
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
|
||||
expand_active_table_node distributor.name
|
||||
expect(page).to have_content producer.name
|
||||
open_enterprise_modal producer
|
||||
modal_should_be_open_for producer
|
||||
|
||||
within ".reveal-modal" do
|
||||
expect(page).to have_content 'Fruit' # Taxon
|
||||
expect(page).to have_content 'Organic' # Producer property
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def click_link_and_ensure(link_text, check)
|
||||
|
||||
@@ -93,5 +93,24 @@ module OpenFoodNetwork
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "columns are aligned" do
|
||||
let(:d1) { create(:distributor_enterprise) }
|
||||
let(:oc1) { create(:simple_order_cycle) }
|
||||
let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) }
|
||||
let(:li1) { build(:line_item) }
|
||||
let(:user) { create(:admin_user)}
|
||||
|
||||
before { o1.line_items << li1 }
|
||||
|
||||
it 'has aligned columsn' do
|
||||
report_types = ["", "order_cycle_supplier_totals", "order_cycle_supplier_totals_by_distributor", "order_cycle_distributor_totals_by_supplier", "order_cycle_customer_totals"]
|
||||
|
||||
report_types.each do |report_type|
|
||||
report = OrdersAndFulfillmentsReport.new user, report_type: report_type
|
||||
report.header.size.should == report.columns.size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
25
spec/lib/open_food_network/property_merge_spec.rb
Normal file
25
spec/lib/open_food_network/property_merge_spec.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
require 'spec_helper'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe PropertyMerge do
|
||||
let(:p1a) { create(:property, presentation: 'One') }
|
||||
let(:p1b) { create(:property, presentation: 'One') }
|
||||
let(:p2) { create(:property, presentation: 'Two') }
|
||||
|
||||
describe "merging Spree::Properties" do
|
||||
it "merges properties" do
|
||||
expect(PropertyMerge.merge([p1a], [p1b, p2])).to eq [p1a, p2]
|
||||
end
|
||||
end
|
||||
|
||||
describe "merging ProducerProperties and Spree::ProductProperties" do
|
||||
let(:pp1a) { create(:product_property, property: p1a) }
|
||||
let(:pp1b) { create(:producer_property, property: p1b) }
|
||||
let(:pp2) { create(:producer_property, property: p2) }
|
||||
|
||||
it "merges properties" do
|
||||
expect(PropertyMerge.merge([pp1a], [pp1b, pp2])).to eq [pp1a, pp2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -420,7 +420,7 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
context "when product has an inherit_properties value set to true" do
|
||||
context "when product has an inherit_properties value set to false" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:simple_product, supplier: supplier, inherits_properties: false) }
|
||||
|
||||
|
||||
@@ -2,6 +2,36 @@ require 'spec_helper'
|
||||
|
||||
module Spree
|
||||
describe Property do
|
||||
describe "scopes" do
|
||||
describe ".applied_by" do
|
||||
let(:producer) { create(:supplier_enterprise) }
|
||||
let(:producer_other) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:simple_product, supplier: producer) }
|
||||
let(:product_other_producer) { create(:simple_product, supplier: producer_other) }
|
||||
let(:product_other_property) { create(:simple_product, supplier: producer) }
|
||||
let(:property) { product.properties.last }
|
||||
let(:property_other) { product_other_producer.properties.last }
|
||||
|
||||
before do
|
||||
product.set_property 'Organic', 'NASAA 12345'
|
||||
product_other_property.set_property 'Organic', 'NASAA 12345'
|
||||
product_other_producer.set_property 'Biodynamic', 'ASDF 1234'
|
||||
end
|
||||
|
||||
it "returns properties applied to supplied products" do
|
||||
expect(Spree::Property.applied_by(producer)).to eq [property]
|
||||
end
|
||||
|
||||
it "doesn't return properties not applied" do
|
||||
expect(Spree::Property.applied_by(producer)).not_to include property_other
|
||||
end
|
||||
|
||||
it "doesn't return duplicates" do
|
||||
expect(Spree::Property.applied_by(producer).to_a.count).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "callbacks" do
|
||||
let(:property) { product_property.property }
|
||||
let(:product) { product_property.product }
|
||||
|
||||
Reference in New Issue
Block a user