From bf44a1c8627257f02cff86afc9f1262e2312c052 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sat, 4 Apr 2015 17:06:26 +0100 Subject: [PATCH 001/151] Update DB schema to store updatable weight on items sold --- .../api/line_items_controller_decorator.rb | 17 +++++++++++++++++ .../20150305004846_add_weight_to_line_items.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 app/controllers/spree/api/line_items_controller_decorator.rb create mode 100644 db/migrate/20150305004846_add_weight_to_line_items.rb diff --git a/app/controllers/spree/api/line_items_controller_decorator.rb b/app/controllers/spree/api/line_items_controller_decorator.rb new file mode 100644 index 0000000000..f9a025873d --- /dev/null +++ b/app/controllers/spree/api/line_items_controller_decorator.rb @@ -0,0 +1,17 @@ +Spree::Api::LineItemsController.class_eval do + after_filter :apply_enterprise_fees, :only => :update + + def apply_enterprise_fees + authorize! :read, order + order.update_distribution_charge! + end +end + + +#when we update a line item the .update_distribution_charge! is called +# order.should_receive .update_distribution_charge! +# check fails when absent + +# in order model check that .update_distribution_charge! is properly tested. +# think through use cases - existing completed order +# currently likely just used to complete orders so add test case that works on a completed order diff --git a/db/migrate/20150305004846_add_weight_to_line_items.rb b/db/migrate/20150305004846_add_weight_to_line_items.rb new file mode 100644 index 0000000000..a4df1a12f0 --- /dev/null +++ b/db/migrate/20150305004846_add_weight_to_line_items.rb @@ -0,0 +1,5 @@ +class AddWeightToLineItems < ActiveRecord::Migration + def change + add_column :spree_line_items, :unit_value, :decimal, :precision => 8, :scale => 2 + end +end diff --git a/db/schema.rb b/db/schema.rb index 7435fa4e30..bec31e61fc 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 => 20150225232938) do +ActiveRecord::Schema.define(:version => 20150305004846) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -532,6 +532,7 @@ ActiveRecord::Schema.define(:version => 20150225232938) do t.string "currency" t.decimal "distribution_fee", :precision => 10, :scale => 2 t.string "shipping_method_name" + t.decimal "unit_value", :precision => 8, :scale => 2 end add_index "spree_line_items", ["order_id"], :name => "index_line_items_on_order_id" From ffd850c761f16e14b7f3049c8cab8a3dca18db71 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sat, 4 Apr 2015 17:44:02 +0100 Subject: [PATCH 002/151] Adding specs for variable weight adjustment via builk order management --- .../spree/api/line_items_controller_spec.rb | 31 +++++++++++++++++++ .../admin/bulk_order_management_spec.rb | 18 +++++++++-- .../unit/bulk_order_management_spec.js.coffee | 31 +++++++++++++++++-- 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 spec/controllers/spree/api/line_items_controller_spec.rb diff --git a/spec/controllers/spree/api/line_items_controller_spec.rb b/spec/controllers/spree/api/line_items_controller_spec.rb new file mode 100644 index 0000000000..e3c9a262af --- /dev/null +++ b/spec/controllers/spree/api/line_items_controller_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +module Spree + describe Spree::Api::LineItemsController do + render_views + + before do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => current_api_user + end + + def self.make_simple_data! + let!(:order) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now) } + let!(:line_item) { FactoryGirl.create(:line_item, order: order, unit_value: 500) } + end + + #test that when a line item is updated, an order's fees are updated too + context "as an admin user" do + sign_in_as_admin! + make_simple_data! + + context "as a line item is updated" do + it "apply enterprise fees on the item" do + line_item_params = { order_id: order.number, id: line_item.id, line_item: { id: line_item.id, unit_value: 520 }, format: :json} + controller.should_receive(:apply_enterprise_fees).and_return(true) + spree_post :update, line_item_params + end + end + end + end +end diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index c8ad230a00..7aa6449c1e 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -57,7 +57,7 @@ feature %q{ end it "displays a column for order date" do - page.should have_selector "th,date", text: "ORDER DATE", :visible => true + page.should have_selector "th.date", text: "ORDER DATE", :visible => true page.should have_selector "td.date", text: o1.completed_at.strftime("%F %T"), :visible => true page.should have_selector "td.date", text: o2.completed_at.strftime("%F %T"), :visible => true end @@ -141,8 +141,22 @@ feature %q{ admin_user = quick_login_as_admin end + let!(:p1) { FactoryGirl.create(:product_with_option_types, group_buy: true, group_buy_unit_size: 5000, variant_unit: "weight", variants: [FactoryGirl.create(:variant, unit_value: 1000)] ) } + let!(:v1) { p1.variants.first } let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 5 ) } + let!(:li1) { FactoryGirl.create(:line_item, order: o1, variant: v1, :quantity => 5, :unit_value => 1000 ) } + + context "modifying the weight/volume of a line item" do + it "update-pending is added to variable 'price'" do + visit '/admin/orders/bulk_management' + first("div#columns_dropdown", :text => "COLUMNS").click + first("div#columns_dropdown div.menu div.menu_item", text: "Weight/Volume").click + page.should_not have_css "input[name='price'].update-pending" + li1_unit_value_column = find("tr#li_#{li1.id} td.unit_value") + li1_unit_value_column.fill_in "unit_value", :with => 1200 + page.should have_css "input[name='price'].update-pending", :visible => false + end + end context "using column display toggle" do it "shows a column display toggle button, which shows a list of columns when clicked" do diff --git a/spec/javascripts/unit/bulk_order_management_spec.js.coffee b/spec/javascripts/unit/bulk_order_management_spec.js.coffee index 35b9e13d61..76ae925785 100644 --- a/spec/javascripts/unit/bulk_order_management_spec.js.coffee +++ b/spec/javascripts/unit/bulk_order_management_spec.js.coffee @@ -33,8 +33,8 @@ describe "AdminOrderMgmtCtrl", -> httpBackend.flush() expect(scope.suppliers).toEqual [{ id : '0', name : 'All' }, 'list of suppliers'] - expect(scope.distributors).toEqual [ { id : '0', name : 'All' }, 'list of distributors' ] - expect(scope.orderCycles).toEqual [ { id : '0', name : 'All' }, 'oc1', 'oc2', 'oc3' ] + expect(scope.distributors).toEqual [ { id : '0', name : 'All' }, 'list of distributors' ] + expect(scope.orderCycles).toEqual [ { id : '0', name : 'All' }, 'oc1', 'oc2', 'oc3' ] expect(scope.initialiseVariables.calls.length).toBe 1 expect(scope.fetchOrders.calls.length).toBe 1 @@ -350,6 +350,33 @@ describe "AdminOrderMgmtCtrl", -> spyOn(VariantUnitManager, "getUnitName").andReturn "kg" expect(scope.formattedValueWithUnitName(2000,unitsVariant)).toEqual "2 kg" + describe "updating the price upon updating the weight of a line item", -> + + it "resets the weight if the weight is set to zero", -> + scope.filteredLineItems = [ + { units_variant: { unit_value: 100 }, price: 2, unit_value: 0 } + ] + expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], 100)).toEqual scope.filteredLineItems[0].price + + it "updates the price if the weight is changed", -> + scope.filteredLineItems = [ + { units_variant: { unit_value: 100 }, price: 2, unit_value: 200 } + ] + old_value = scope.filteredLineItems[0].units_variant.unit_value + new_value = scope.filteredLineItems[0].unit_value + sp = scope.filteredLineItems[0].price * new_value / old_value + expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], old_value)).toEqual sp + + it "doesn't update the price if the weight is not changed", -> + scope.filteredLineItems = [ + { units_variant: { unit_value: 100 }, price: 2, unit_value: 100 } + ] + old_value = scope.filteredLineItems[0].unit_value + new_value = scope.filteredLineItems[0].unit_value + sp = scope.filteredLineItems[0].price + expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], old_value)).toEqual sp + + describe "managing pending changes", -> dataSubmitter = pendingChangesService = null From ff935af18bd4a2a459e945dafa1e35642ac5d198 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sat, 4 Apr 2015 17:52:31 +0100 Subject: [PATCH 003/151] Variable Weights: Adding ability to update the weight/volume of a line_item after checkout. The price of the line_item is automatically updated to reflect the value of the new weight. --- .../admin/bulk_order_management.js.coffee | 9 +- .../directives/line_item_upd_attr.js.coffee | 6 +- app/models/spree/line_item_decorator.rb | 3 +- .../admin/orders/bulk_management.html.haml | 107 ++++++++++-------- .../spree/api/line_items/bulk_show.v1.rabl | 6 +- 5 files changed, 75 insertions(+), 56 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 4c1a319c1a..a20cdd27e7 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -32,7 +32,8 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ variant: { name: "Variant", visible: true } quantity: { name: "Quantity", visible: true } max: { name: "Max", visible: true } - + unit_value: { name: "Weight/Volume", visible: false } + price: { name: "Price", visible: false } $scope.initialise = -> $scope.initialiseVariables() authorise_api_reponse = "" @@ -162,6 +163,12 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.supplierFilter = $scope.suppliers[0].id $scope.orderCycleFilter = $scope.orderCycles[0].id $scope.quickSearch = "" + + $scope.weightAdjustedPrice = (lineItem, oldValue) -> + if lineItem.unit_value <= 0 + lineItem.unit_value = lineItem.units_variant.unit_value + lineItem.price = lineItem.price * lineItem.unit_value / oldValue + #$scope.bulk_order_form.line_item.price.$setViewValue($scope.bulk_order_form.line_item.price.$viewValue) ] daysFromToday = (days) -> diff --git a/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee b/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee index c83d7fdc0f..c5afce07a5 100644 --- a/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee +++ b/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee @@ -8,7 +8,9 @@ angular.module("ofn.admin").directive "ofnLineItemUpdAttr", [ scope.$watch -> scope.$eval(attrs.ngModel) , (value) -> - if ngModel.$dirty + #if ngModel.$dirty + # i think i can take this out, this directive is still only called + # on a change and only an updated value will create a db call. if value == element.dbValue pendingChanges.remove(scope.line_item.id, attrName) switchClass( element, "", ["update-pending", "update-error", "update-success"], false ) @@ -20,4 +22,4 @@ angular.module("ofn.admin").directive "ofnLineItemUpdAttr", [ url: "/api/orders/#{scope.line_item.order.number}/line_items/#{scope.line_item.id}?line_item[#{attrName}]=#{value}" pendingChanges.add(scope.line_item.id, attrName, changeObj) switchClass( element, "update-pending", ["update-error", "update-success"], false ) -] \ No newline at end of file +] diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index b2c0a583fc..4eec0bcd2b 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -1,5 +1,6 @@ Spree::LineItem.class_eval do - attr_accessible :max_quantity + attr_accessible :max_quantity, :unit_value + attr_accessible :unit_value, :price, :as => :api # -- Scopes scope :managed_by, lambda { |user| diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 61bf9fbb7e..a1f53826e1 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -100,53 +100,60 @@ %div{ :class => "sixteen columns alpha", 'ng-show' => '!loading && filteredLineItems.length == 0'} %h1#no_results No orders found. %div{ 'ng-hide' => 'loading || filteredLineItems.length == 0' } - %table.index#listing_orders.bulk{ :class => "sixteen columns alpha" } - %thead - %tr - %th.bulk - %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } - %th.order_no{ 'ng-show' => 'columns.order_no.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.number'; reverse = !reverse" } Order No. - %th.full_name{ 'ng-show' => 'columns.full_name.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.full_name'; reverse = !reverse" } Name - %th.email{ 'ng-show' => 'columns.email.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.email'; reverse = !reverse" } Email - %th.phone{ 'ng-show' => 'columns.phone.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } Phone - %th.date{ 'ng-show' => 'columns.order_date.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } Order Date - %th.producer{ 'ng-show' => 'columns.producer.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'supplier.name'; reverse = !reverse" } Producer - %th.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.order_cycle.name'; reverse = !reverse" } Order Cycle - %th.hub{ 'ng-show' => 'columns.hub.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } Hub - %th.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'units_variant.unit_text'; reverse = !reverse" } Product: Unit - %th.quantity{ 'ng-show' => 'columns.quantity.visible' } Quantity - %th.max{ 'ng-show' => 'columns.max.visible' } Max - %th.actions - %th.actions - Ask?  - %input{ :type => 'checkbox', 'ng-model' => "confirmDelete" } - %tr.line_item{ 'ng-repeat' => "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:predicate:reverse )", 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", :id => "li_{{line_item.id}}" } - %td.bulk - %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked' } - %td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }} - %td.full_name{ 'ng-show' => 'columns.full_name.visible' } {{ line_item.order.full_name }} - %td.email{ 'ng-show' => 'columns.email.visible' } {{ line_item.order.email }} - %td.phone{ 'ng-show' => 'columns.phone.visible' } {{ line_item.order.phone }} - %td.date{ 'ng-show' => 'columns.order_date.visible' } {{ line_item.order.completed_at }} - %td.producer{ 'ng-show' => 'columns.producer.visible' } {{ line_item.supplier.name }} - %td.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } {{ line_item.order.order_cycle.name }} - %td.hub{ 'ng-show' => 'columns.hub.visible' } {{ line_item.order.distributor.name }} - %td.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.unit_text }} - %td.quantity{ 'ng-show' => 'columns.quantity.visible' } - %input{ :type => 'number', :name => 'quantity', 'ng-model' => "line_item.quantity", 'ofn-line-item-upd-attr' => "quantity" } - %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} - %td.actions - %a{ :class => "edit-order icon-edit no-text", 'ofn-confirm-link-path' => "/admin/orders/{{line_item.order.number}}/edit" } - %td.actions - %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } - %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'pendingChanges.submitAll()' } \ No newline at end of file + %form{ 'ng-model' => "bulk_order_form" } + %table.index#listing_orders.bulk{ :class => "sixteen columns alpha" } + %thead + %tr + %th.bulk + %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } + %th.order_no{ 'ng-show' => 'columns.order_no.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.number'; reverse = !reverse" } Order No. + %th.full_name{ 'ng-show' => 'columns.full_name.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.full_name'; reverse = !reverse" } Name + %th.email{ 'ng-show' => 'columns.email.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.email'; reverse = !reverse" } Email + %th.phone{ 'ng-show' => 'columns.phone.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } Phone + %th.date{ 'ng-show' => 'columns.order_date.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } Order Date + %th.producer{ 'ng-show' => 'columns.producer.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'supplier.name'; reverse = !reverse" } Producer + %th.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.order_cycle.name'; reverse = !reverse" } Order Cycle + %th.hub{ 'ng-show' => 'columns.hub.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } Hub + %th.variant{ 'ng-show' => 'columns.variant.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'units_variant.unit_text'; reverse = !reverse" } Product: Unit + %th.quantity{ 'ng-show' => 'columns.quantity.visible' } Quantity + %th.max{ 'ng-show' => 'columns.max.visible' } Max + %th.unit_value{ 'ng-show' => 'columns.unit_value.visible' } Weight/Volume + %th.price{ 'ng-show' => 'columns.price.visible' } Price + %th.actions + %th.actions + Ask?  + %input{ :type => 'checkbox', 'ng-model' => "confirmDelete" } + %tr.line_item{ 'ng-repeat' => "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:predicate:reverse )", 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", :id => "li_{{line_item.id}}" } + %td.bulk + %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked' } + %td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }} + %td.full_name{ 'ng-show' => 'columns.full_name.visible' } {{ line_item.order.full_name }} + %td.email{ 'ng-show' => 'columns.email.visible' } {{ line_item.order.email }} + %td.phone{ 'ng-show' => 'columns.phone.visible' } {{ line_item.order.phone }} + %td.date{ 'ng-show' => 'columns.order_date.visible' } {{ line_item.order.completed_at }} + %td.producer{ 'ng-show' => 'columns.producer.visible' } {{ line_item.supplier.name }} + %td.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } {{ line_item.order.order_cycle.name }} + %td.hub{ 'ng-show' => 'columns.hub.visible' } {{ line_item.order.distributor.name }} + %td.variant{ 'ng-show' => 'columns.variant.visible' } + %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.unit_text }} + %td.quantity{ 'ng-show' => 'columns.quantity.visible' } + %input{ :type => 'number', :name => 'quantity', 'ng-model' => "line_item.quantity", 'ofn-line-item-upd-attr' => "quantity" } + %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} + %td.unit_value{ 'ng-show' => 'columns.unit_value.visible' } + %input{ :type => 'number', :name => 'unit_value', :id => 'unit_value', 'ng-model' => "line_item.unit_value", 'ng-change' => "weightAdjustedPrice(line_item, {{ line_item.unit_value }})", 'ofn-line-item-upd-attr' => "unit_value" } + %td.price{ 'ng-show' => 'columns.price.visible' } + %input{ :type => 'text', :name => 'price', :id => 'price', :value => '{{ line_item.price | currency }}', 'ng-model' => "line_item.price", 'ng-readonly' => "true", 'ofn-line-item-upd-attr' => "price" } + %td.actions + %a{ :class => "edit-order icon-edit no-text", 'ofn-confirm-link-path' => "/admin/orders/{{line_item.order.number}}/edit" } + %td.actions + %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } + %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'pendingChanges.submitAll()' } diff --git a/app/views/spree/api/line_items/bulk_show.v1.rabl b/app/views/spree/api/line_items/bulk_show.v1.rabl index 8b53c42086..6b24ef4bf2 100644 --- a/app/views/spree/api/line_items/bulk_show.v1.rabl +++ b/app/views/spree/api/line_items/bulk_show.v1.rabl @@ -1,5 +1,7 @@ object @line_item -attributes :id, :quantity, :max_quantity +attributes :id, :quantity, :max_quantity, :price node( :supplier ) { |li| partial 'api/enterprises/bulk_show', :object => li.product.supplier } node( :units_product ) { |li| partial 'spree/api/products/units_show', :object => li.product } -node( :units_variant ) { |li| partial 'spree/api/variants/units_show', :object => li.variant } \ No newline at end of file +node( :units_variant ) { |li| partial 'spree/api/variants/units_show', :object => li.variant } +node( :unit_value ) { |li| li.unit_value.to_f } +node( :price ) { |li| li.price } From 83981fbb15e254cacf5c9cacefe0cec4d0f9f787 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 14 Apr 2015 17:29:56 +0100 Subject: [PATCH 004/151] Adding additional logic for if the line_item unit_value is nil --- app/assets/javascripts/admin/bulk_order_management.js.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index a20cdd27e7..a52b770831 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -165,6 +165,8 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.quickSearch = "" $scope.weightAdjustedPrice = (lineItem, oldValue) -> + if oldValue <= 0 + oldValue = lineItem.units_variant.unit_value if lineItem.unit_value <= 0 lineItem.unit_value = lineItem.units_variant.unit_value lineItem.price = lineItem.price * lineItem.unit_value / oldValue From 3f01a459ac317a5fef7ac6b753bfcf6eca074bf8 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 26 Sep 2014 12:17:55 +1000 Subject: [PATCH 005/151] Adding a standard variant upon initialisation of a new instance of Spree::Product --- app/models/spree/product_decorator.rb | 4 ++++ spec/models/spree/product_spec.rb | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index cf7d02bf46..034bbed043 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -32,6 +32,7 @@ Spree::Product.class_eval do validates_presence_of :variant_unit_name, if: -> p { p.variant_unit == 'items' } + after_initialize :ensure_first_standard_variant, :if => :new_record? after_initialize :set_available_on_to_now, :if => :new_record? after_save :update_units after_touch :touch_distributors @@ -205,4 +206,7 @@ Spree::Product.class_eval do Spree::OptionType.where('name LIKE ?', 'unit_%%') end + def ensure_first_standard_variant + self.variants << Spree::Variant.new + end end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 400f92754b..73c6f2fe10 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -85,6 +85,14 @@ module Spree end end + context "instatiating a new product" do + let!(:product) { Spree::Product.new } + + it "creates a standard (non-master) variant when created" do + product.variants.should_not be_empty + end + end + context "when the unit is items" do it "is valid when unit name is set and unit scale is not" do product.variant_unit = 'items' @@ -122,7 +130,6 @@ module Spree end end end - describe "scopes" do describe "in_supplier" do From dfb513cce7943fcb59dab66d35daaeb0a405383d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 26 Sep 2014 13:11:32 +1000 Subject: [PATCH 006/151] Use after_create callback to duplicate master variant --- app/models/spree/product_decorator.rb | 9 ++++++--- spec/models/spree/product_spec.rb | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 034bbed043..aae90f41c4 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -32,7 +32,7 @@ Spree::Product.class_eval do validates_presence_of :variant_unit_name, if: -> p { p.variant_unit == 'items' } - after_initialize :ensure_first_standard_variant, :if => :new_record? + after_create :ensure_one_standard_variant after_initialize :set_available_on_to_now, :if => :new_record? after_save :update_units after_touch :touch_distributors @@ -206,7 +206,10 @@ Spree::Product.class_eval do Spree::OptionType.where('name LIKE ?', 'unit_%%') end - def ensure_first_standard_variant - self.variants << Spree::Variant.new + def ensure_one_standard_variant + variant = self.master.dup + variant.product = self + variant.is_master = false + self.variants << variant end end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 73c6f2fe10..e3a72f19c5 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -85,11 +85,21 @@ module Spree end end - context "instatiating a new product" do - let!(:product) { Spree::Product.new } + context "saving a new product" do + let!(:product){ Spree::Product.new } - it "creates a standard (non-master) variant when created" do - product.variants.should_not be_empty + before do + product.primary_taxon = create(:taxon) + product.supplier = create(:supplier_enterprise) + product.name = "Product1" + product.on_hand = 3 + product.price = 4.27 + product.save! + end + + it "copies the properties on master variant to the first standard variant" do + standard_variant = product.variants(:reload).first + expect(standard_variant.price).to eq product.master.price end end From 0a7b01ff07f36891f39a99fa8850ca143fa96b56 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 16:13:03 +1000 Subject: [PATCH 007/151] Product requires variant_unit and master requires unit_value and/or unit_desc --- app/models/spree/product_decorator.rb | 6 +- app/models/spree/variant_decorator.rb | 6 +- spec/factories.rb | 4 + spec/models/spree/product_spec.rb | 106 +++++--------------------- spec/models/spree/variant_spec.rb | 62 ++------------- 5 files changed, 35 insertions(+), 149 deletions(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index aae90f41c4..b6cd0bb221 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -25,14 +25,14 @@ Spree::Product.class_eval do validates_presence_of :supplier validates :primary_taxon, presence: { message: "^Product Category can't be blank" } validates :tax_category_id, presence: { message: "^Tax Category can't be blank" }, if: "Spree::Config.products_require_tax_category" - - validates_presence_of :variant_unit, if: :has_variants? + + validates_presence_of :variant_unit validates_presence_of :variant_unit_scale, if: -> p { %w(weight volume).include? p.variant_unit } validates_presence_of :variant_unit_name, if: -> p { p.variant_unit == 'items' } - after_create :ensure_one_standard_variant + #after_create :ensure_one_standard_variant after_initialize :set_available_on_to_now, :if => :new_record? after_save :update_units after_touch :touch_distributors diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 86a0b5fd69..703111c884 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -12,12 +12,10 @@ Spree::Variant.class_eval do accepts_nested_attributes_for :images validates_presence_of :unit_value, - if: -> v { %w(weight volume).include? v.product.andand.variant_unit }, - unless: :is_master + if: -> v { %w(weight volume).include? v.product.andand.variant_unit } validates_presence_of :unit_description, - if: -> v { v.product.andand.variant_unit.present? && v.unit_value.nil? }, - unless: :is_master + if: -> v { v.product.andand.variant_unit.present? && v.unit_value.nil? } before_validation :update_weight_from_unit_value, if: -> v { v.product.present? } after_save :update_units diff --git a/spec/factories.rb b/spec/factories.rb index aa86af8563..7effe49a9d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -214,6 +214,10 @@ end FactoryGirl.modify do + factory :base_product do + unit_value 1 + unit_description '' + end factory :product do primary_taxon { Spree::Taxon.first || FactoryGirl.create(:taxon) } end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index e3a72f19c5..aca9404fb7 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -85,23 +85,24 @@ module Spree end end - context "saving a new product" do - let!(:product){ Spree::Product.new } - - before do - product.primary_taxon = create(:taxon) - product.supplier = create(:supplier_enterprise) - product.name = "Product1" - product.on_hand = 3 - product.price = 4.27 - product.save! - end - - it "copies the properties on master variant to the first standard variant" do - standard_variant = product.variants(:reload).first - expect(standard_variant.price).to eq product.master.price - end - end + # context "saving a new product" do + # let!(:product){ Spree::Product.new } + # + # before do + # product.primary_taxon = create(:taxon) + # product.supplier = create(:supplier_enterprise) + # product.name = "Product1" + # product.variant_unit = "weight" + # product.on_hand = 3 + # product.price = 4.27 + # product.save! + # end + # + # it "copies the properties on master variant to the first standard variant" do + # standard_variant = product.variants(:reload).first + # expect(standard_variant.price).to eq product.master.price + # end + # end context "when the unit is items" do it "is valid when unit name is set and unit scale is not" do @@ -128,7 +129,7 @@ module Spree product.variant_unit_name = nil product.variant_unit_scale = nil - product.should be_valid + expect(product).to be_invalid end it "requires a unit scale when variant unit is weight" do @@ -412,63 +413,6 @@ module Spree describe "variant units" do - context "when the product initially has no variant unit" do - let!(:p) { create(:simple_product, - variant_unit: nil, - variant_unit_scale: nil, - variant_unit_name: nil) } - - context "when the required option type does not exist" do - it "creates the option type and assigns it to the product" do - expect { - p.update_attributes!(variant_unit: 'weight', variant_unit_scale: 1000) - }.to change(Spree::OptionType, :count).by(1) - - ot = Spree::OptionType.last - ot.name.should == 'unit_weight' - ot.presentation.should == 'Weight' - - p.option_types.should == [ot] - end - - it "does the same with volume" do - expect { - p.update_attributes!(variant_unit: 'volume', variant_unit_scale: 1000) - }.to change(Spree::OptionType, :count).by(1) - - ot = Spree::OptionType.last - ot.name.should == 'unit_volume' - ot.presentation.should == 'Volume' - - p.option_types.should == [ot] - end - - it "does the same with items" do - expect { - p.update_attributes!(variant_unit: 'items', variant_unit_name: 'packet') - }.to change(Spree::OptionType, :count).by(1) - - ot = Spree::OptionType.last - ot.name.should == 'unit_items' - ot.presentation.should == 'Items' - - p.option_types.should == [ot] - end - end - - context "when the required option type already exists" do - let!(:ot) { create(:option_type, name: 'unit_weight', presentation: 'Weight') } - - it "looks up the option type and assigns it to the product" do - expect { - p.update_attributes!(variant_unit: 'weight', variant_unit_scale: 1000) - }.to change(Spree::OptionType, :count).by(0) - - p.option_types.should == [ot] - end - end - end - context "when the product already has a variant unit set (and all required option types exist)" do let!(:p) { create(:simple_product, variant_unit: 'weight', @@ -482,11 +426,6 @@ module Spree p.option_types.should == [ot_volume] end - it "leaves option type unassigned if none is provided" do - p.update_attributes!(variant_unit: nil, variant_unit_scale: nil) - p.option_types.should == [] - end - it "does not remove and re-add the option type if it is not changed" do p.option_types.should_receive(:delete).never p.update_attributes!(name: 'foo') @@ -523,13 +462,6 @@ module Spree end end - describe "returning the variant unit option type" do - it "returns nil when variant_unit is not set" do - p = create(:simple_product, variant_unit: nil) - p.variant_unit_option_type.should be_nil - end - end - it "finds all variant unit option types" do ot1 = create(:option_type, name: 'unit_weight', presentation: 'Weight') ot2 = create(:option_type, name: 'unit_volume', presentation: 'Volume') diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 5a82f04ba7..aa3296e5dc 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -240,12 +240,14 @@ module Spree end context "when the product does not have variants" do - let(:product) { create(:simple_product, variant_unit: nil) } + let(:product) { create(:simple_product) } let(:variant) { product.master } - it "does not require unit value or unit description when the product's unit is empty" do + it "requires unit value and/or unit description" do variant.unit_value = nil variant.unit_description = nil + variant.should be_invalid + variant.unit_value = 1 variant.should be_valid end end @@ -283,7 +285,7 @@ module Spree describe "setting the variant's weight from the unit value" do it "sets the variant's weight when unit is weight" do - p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil) + p = create(:simple_product, variant_unit: 'volume') v = create(:variant, product: p, weight: nil) p.update_attributes! variant_unit: 'weight', variant_unit_scale: 1 @@ -293,7 +295,7 @@ module Spree end it "does nothing when unit is not weight" do - p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil) + p = create(:simple_product, variant_unit: 'volume') v = create(:variant, product: p, weight: 123) p.update_attributes! variant_unit: 'volume', variant_unit_scale: 1 @@ -303,7 +305,7 @@ module Spree end it "does nothing when unit_value is not set" do - p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil) + p = create(:simple_product, variant_unit: 'volume') v = create(:variant, product: p, weight: 123) p.update_attributes! variant_unit: 'weight', variant_unit_scale: 1 @@ -316,56 +318,6 @@ module Spree end end - context "when the variant initially has no value" do - context "when the required option value does not exist" do - let!(:p) { create(:simple_product, variant_unit: nil, variant_unit_scale: nil) } - let!(:v) { create(:variant, product: p, unit_value: nil, unit_description: nil) } - - before do - p.update_attributes!(variant_unit: 'weight', variant_unit_scale: 1) - @ot = Spree::OptionType.find_by_name 'unit_weight' - end - - it "creates the option value and assigns it to the variant" do - expect { - v.update_attributes!(unit_value: 10, unit_description: 'foo') - }.to change(Spree::OptionValue, :count).by(1) - - ov = Spree::OptionValue.last - ov.option_type.should == @ot - ov.name.should == '10g foo' - ov.presentation.should == '10g foo' - - v.option_values.should include ov - end - end - - context "when the required option value already exists" do - let!(:p_orig) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } - let!(:v_orig) { create(:variant, product: p_orig, unit_value: 10, unit_description: 'foo') } - - let!(:p) { create(:simple_product, variant_unit: nil, variant_unit_scale: nil) } - let!(:v) { create(:variant, product: p, unit_value: nil, unit_description: nil) } - - before do - p.update_attributes!(variant_unit: 'weight', variant_unit_scale: 1) - @ot = Spree::OptionType.find_by_name 'unit_weight' - end - - it "looks up the option value and assigns it to the variant" do - expect { - v.update_attributes!(unit_value: 10, unit_description: 'foo') - }.to change(Spree::OptionValue, :count).by(0) - - ov = v.option_values.last - ov.option_type.should == @ot - ov.name.should == '10g foo' - ov.presentation.should == '10g foo' - - v_orig.option_values.should include ov - end - end - end context "when the variant already has a value set (and all required option values exist)" do let!(:p0) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } let!(:v0) { create(:variant, product: p0, unit_value: 10, unit_description: 'foo') } From 13a910c3727755bbf174cd3ef869ccb40979459e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 17:08:18 +1000 Subject: [PATCH 008/151] Replace validates_associated on master with current spree method for error reporting master saves --- app/models/spree/product_decorator.rb | 20 +++++++++++++++++++- spec/models/spree/product_spec.rb | 5 +++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index b6cd0bb221..258d1e0df2 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -21,7 +21,6 @@ Spree::Product.class_eval do attr_accessible :supplier_id, :primary_taxon_id, :distributor_ids, :product_distributions_attributes, :group_buy, :group_buy_unit_size attr_accessible :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value, :unit_description, :notes, :images_attributes, :display_as - validates_associated :master, message: "^Price and On Hand must be valid" validates_presence_of :supplier validates :primary_taxon, presence: { message: "^Product Category can't be blank" } validates :tax_category_id, presence: { message: "^Tax Category can't be blank" }, if: "Spree::Config.products_require_tax_category" @@ -212,4 +211,23 @@ Spree::Product.class_eval do variant.is_master = false self.variants << variant end + + # Override Spree's old save_master method and replace it with the most recent method from spree repository + # This fixes any problems arising from failing master saves, without the need for a validates_associated on + # master, while giving us more specific errors as to why saving failed + def save_master + begin + if master && (master.changed? || master.new_record? || (master.default_price && (master.default_price.changed? || master.default_price.new_record?))) + master.save! + end + + # If the master cannot be saved, the Product object will get its errors + # and will be destroyed + rescue ActiveRecord::RecordInvalid + master.errors.each do |att, error| + self.errors.add(att, error) + end + raise + end + end end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index aca9404fb7..70bfb38227 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -25,9 +25,10 @@ module Spree it "does not save when master is invalid" do s = create(:supplier_enterprise) t = create(:taxon) - product = Product.new supplier_id: s.id, name: "Apples", price: 1, primary_taxon_id: t.id + product = Product.new supplier_id: s.id, name: "Apples", price: 1, primary_taxon_id: t.id, variant_unit: "weight", variant_unit_scale: 1000, unit_value: 1 product.on_hand = "10,000" - product.save.should be_false + expect(product.save).to be_false + expect(product.errors[:count_on_hand]).to include "is not a number" end it "defaults available_on to now" do From 869551a17c2b033061c197e9cbd48ed2e302658a Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 17:22:30 +1000 Subject: [PATCH 009/151] Adding a standard variant again --- app/models/spree/product_decorator.rb | 5 ++-- spec/models/spree/product_spec.rb | 39 ++++++++++++++------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 258d1e0df2..c83ffd0c0a 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -21,6 +21,8 @@ Spree::Product.class_eval do attr_accessible :supplier_id, :primary_taxon_id, :distributor_ids, :product_distributions_attributes, :group_buy, :group_buy_unit_size attr_accessible :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value, :unit_description, :notes, :images_attributes, :display_as + before_validation :ensure_standard_variant + validates_presence_of :supplier validates :primary_taxon, presence: { message: "^Product Category can't be blank" } validates :tax_category_id, presence: { message: "^Tax Category can't be blank" }, if: "Spree::Config.products_require_tax_category" @@ -31,7 +33,6 @@ Spree::Product.class_eval do validates_presence_of :variant_unit_name, if: -> p { p.variant_unit == 'items' } - #after_create :ensure_one_standard_variant after_initialize :set_available_on_to_now, :if => :new_record? after_save :update_units after_touch :touch_distributors @@ -205,7 +206,7 @@ Spree::Product.class_eval do Spree::OptionType.where('name LIKE ?', 'unit_%%') end - def ensure_one_standard_variant + def ensure_standard_variant variant = self.master.dup variant.product = self variant.is_master = false diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 70bfb38227..2160b42b5b 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -86,24 +86,27 @@ module Spree end end - # context "saving a new product" do - # let!(:product){ Spree::Product.new } - # - # before do - # product.primary_taxon = create(:taxon) - # product.supplier = create(:supplier_enterprise) - # product.name = "Product1" - # product.variant_unit = "weight" - # product.on_hand = 3 - # product.price = 4.27 - # product.save! - # end - # - # it "copies the properties on master variant to the first standard variant" do - # standard_variant = product.variants(:reload).first - # expect(standard_variant.price).to eq product.master.price - # end - # end + context "saving a new product" do + let!(:product){ Spree::Product.new } + + before do + product.primary_taxon = create(:taxon) + product.supplier = create(:supplier_enterprise) + product.name = "Product1" + product.variant_unit = "weight" + product.variant_unit_scale = 1000 + product.unit_value = 1 + product.on_hand = 3 + product.price = 4.27 + product.save! + end + + it "copies the properties on master variant to the first standard variant" do + expect(product.variants(:reload).length).to eq 1 + standard_variant = product.variants(:reload).first + expect(standard_variant.price).to eq product.master.price + end + end context "when the unit is items" do it "is valid when unit name is set and unit scale is not" do From 28486f9e76130e6381d45bd9df91b5031e446849 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 17:24:10 +1000 Subject: [PATCH 010/151] Only adds standard variant on create --- app/models/spree/product_decorator.rb | 2 +- spec/models/spree/product_spec.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index c83ffd0c0a..bb246ace59 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -21,7 +21,7 @@ Spree::Product.class_eval do attr_accessible :supplier_id, :primary_taxon_id, :distributor_ids, :product_distributions_attributes, :group_buy, :group_buy_unit_size attr_accessible :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value, :unit_description, :notes, :images_attributes, :display_as - before_validation :ensure_standard_variant + before_validation :ensure_standard_variant, if: :new_record? validates_presence_of :supplier validates :primary_taxon, presence: { message: "^Product Category can't be blank" } diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 2160b42b5b..7eb5b777ac 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -106,6 +106,11 @@ module Spree standard_variant = product.variants(:reload).first expect(standard_variant.price).to eq product.master.price end + + it "only duplicates master variant on initial save" do + product.save! + expect(product.variants(:reload).length).to eq 1 + end end context "when the unit is items" do From 971723964e05d8d576d20be9aeb2dd5d1e02a5db Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 17:49:57 +1000 Subject: [PATCH 011/151] Update outdated spec --- spec/models/spree/product_spec.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 7eb5b777ac..57327b75ad 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -28,7 +28,12 @@ module Spree product = Product.new supplier_id: s.id, name: "Apples", price: 1, primary_taxon_id: t.id, variant_unit: "weight", variant_unit_scale: 1000, unit_value: 1 product.on_hand = "10,000" expect(product.save).to be_false - expect(product.errors[:count_on_hand]).to include "is not a number" + + # It is expected that master variant :count_on_hand should cause the failure to save, and it does. However, this broke with the addition of + # the :ensure_standard_variant callback. Evidently, normal variants are checked and their errors added to the product model before the + # save_master callback on product is run. Therefore, the error that is raised here is one on a normal variant, rather than one on master. + # expect(product.errors[:count_on_hand]).to include "is not a number" + expect(product.errors[:"variants.count_on_hand"]).to include "is not a number" end it "defaults available_on to now" do From 89afbc80a6953a984854eb17f4e4975087d1f043 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 18:01:46 +1000 Subject: [PATCH 012/151] Set initial on_hand to 0 --- spec/models/spree/variant_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index aa3296e5dc..eec2868453 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -14,7 +14,7 @@ module Spree describe "finding variants in stock" do before do - p = create(:product) + p = create(:product, on_hand: 0) @v_in_stock = create(:variant, product: p) @v_on_demand = create(:variant, product: p, on_demand: true) @v_no_stock = create(:variant, product: p) From 5e2fe56c228ed1f1a7d7b40fc22f4de74529fba3 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 18:05:24 +1000 Subject: [PATCH 013/151] Cleanup --- spec/models/spree/product_spec.rb | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 57327b75ad..07b67ce872 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -135,10 +135,10 @@ module Spree end end - context "when product does not have variants" do + context "a basic product" do let(:product) { create(:simple_product) } - it "does not require any variant unit fields" do + it "requires variant unit fields" do product.variant_unit = nil product.variant_unit_name = nil product.variant_unit_scale = nil @@ -523,16 +523,6 @@ module Spree end describe "finding products in stock for a particular distribution" do - it "returns in-stock products without variants" do - p = create(:simple_product) - p.master.update_attribute(:count_on_hand, 1) - d = create(:distributor_enterprise) - oc = create(:simple_order_cycle, distributors: [d]) - oc.exchanges.outgoing.first.variants << p.master - - p.should have_stock_for_distribution(oc, d) - end - it "returns on-demand products" do p = create(:simple_product, on_demand: true) p.master.update_attribute(:count_on_hand, 0) From a223a2d662a44896b7fa1615dccda1751895bf50 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 18:32:40 +1000 Subject: [PATCH 014/151] Cannot remove all variants from a product --- app/models/spree/product_decorator.rb | 1 + spec/models/spree/product_spec.rb | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index bb246ace59..3d35700b7c 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -23,6 +23,7 @@ Spree::Product.class_eval do before_validation :ensure_standard_variant, if: :new_record? + validates_presence_of :variants, message: "Product must have at least one variant" validates_presence_of :supplier validates :primary_taxon, presence: { message: "^Product Category can't be blank" } validates :tax_category_id, presence: { message: "^Tax Category can't be blank" }, if: "Spree::Config.products_require_tax_category" diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 07b67ce872..35dd847d1f 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -8,7 +8,7 @@ module Spree it { should belong_to(:primary_taxon) } it { should have_many(:product_distributions) } end - + describe "validations and defaults" do it "is valid when built from factory" do build(:product).should be_valid @@ -61,6 +61,14 @@ module Spree end + it "does not allow only standard variant to be deleted" do + product = create(:simple_product) + expect(product.variants(:reload).length).to eq 1 + product.variants = [] + expect(product.save).to be_false + expect(product.errors[:variants]).to include "Product must have at least one variant" + end + context "when the product has variants" do let(:product) do product = create(:simple_product) From fcb3bc894b18fb6e43a1b00f5b4413b19a3173b6 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 2 Oct 2014 18:35:37 +1000 Subject: [PATCH 015/151] Cleanup --- spec/models/spree/variant_spec.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index eec2868453..c038bf4598 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -239,19 +239,6 @@ module Spree end end - context "when the product does not have variants" do - let(:product) { create(:simple_product) } - let(:variant) { product.master } - - it "requires unit value and/or unit description" do - variant.unit_value = nil - variant.unit_description = nil - variant.should be_invalid - variant.unit_value = 1 - variant.should be_valid - end - end - describe "unit value/description" do describe "getting name for display" do it "returns display_name if present" do From 2b47c9145a9b18f346dd186ef4254dd062eb0648 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 3 Oct 2014 13:59:34 +1000 Subject: [PATCH 016/151] Cannot delete last variant of product --- app/models/spree/variant_decorator.rb | 8 ++++++++ spec/models/spree/variant_spec.rb | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 703111c884..ee05b3b2b8 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -18,6 +18,7 @@ Spree::Variant.class_eval do if: -> v { v.product.andand.variant_unit.present? && v.unit_value.nil? } before_validation :update_weight_from_unit_value, if: -> v { v.product.present? } + before_destroy :check_product_variants after_save :update_units scope :with_order_cycles_inner, joins(exchanges: :order_cycle) @@ -123,4 +124,11 @@ Spree::Variant.class_eval do option_value_namer.name end end + + def check_product_variants + if product.variants == [self] # Only variant left on product + errors.add :product, "must have at least one variant" + return false + end + end end diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index c038bf4598..2c37065916 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -399,5 +399,20 @@ module Spree v.destroy e.reload.variant_ids.should be_empty end + + context "as the last variant of a product" do + let!(:product) { create(:simple_product) } + let!(:first_variant) { product.variants(:reload).first } + let!(:extra_variant) { create(:variant, product: product) } + + it "cannot be deleted" do + expect(product.variants(:reload).length).to eq 2 + expect(extra_variant.destroy).to eq extra_variant + expect(product.variants(:reload).length).to eq 1 + expect(first_variant.destroy).to be_false + expect(product.variants(:reload).length).to eq 1 + expect(first_variant.errors[:product]).to include "must have at least one variant" + end + end end end From 4b182f92484a138c31620977c3e5b40b6efccb9c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 3 Oct 2014 14:08:03 +1000 Subject: [PATCH 017/151] Can't delete final variant on a product from BPE --- .../admin/bulk_product_update.js.coffee | 19 +++++++++------- .../unit/bulk_product_update_spec.js.coffee | 22 +++++++++++++++---- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index e8e14413ae..5ec6eb52f8 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -135,15 +135,18 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.deleteVariant = (product, variant) -> - if !$scope.variantSaved(variant) - $scope.removeVariant(product, variant) + if product.variants.length > 1 + if !$scope.variantSaved(variant) + $scope.removeVariant(product, variant) + else + if confirm("Are you sure?") + $http( + method: "DELETE" + url: "/api/products/" + product.permalink_live + "/variants/" + variant.id + "/soft_delete" + ).success (data) -> + $scope.removeVariant(product, variant) else - if confirm("Are you sure?") - $http( - method: "DELETE" - url: "/api/products/" + product.permalink_live + "/variants/" + variant.id + "/soft_delete" - ).success (data) -> - $scope.removeVariant(product, variant) + alert("The last variant cannot be deleted!") $scope.removeVariant = (product, variant) -> product.variants.splice product.variants.indexOf(variant), 1 diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index a6fb0fa432..eb3830204c 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -777,22 +777,31 @@ describe "AdminProductEditCtrl", -> describe "deleting variants", -> + describe "when the variant is the only one left on the product", -> + it "alerts the user", -> + spyOn(window, "alert") + $scope.products = [ + {id: 1, variants: [{id: 1}]} + ] + $scope.deleteVariant $scope.products[0], $scope.products[0].variants[0] + expect(window.alert).toHaveBeenCalledWith "The last variant cannot be deleted!" + describe "when the variant has not been saved", -> it "removes the variant from products and dirtyProducts", -> spyOn(window, "confirm").andReturn true $scope.products = [ - {id: 1, variants: [{id: -1}]} + {id: 1, variants: [{id: -1},{id: -2}]} ] DirtyProducts.addVariantProperty 1, -1, "something", "something" DirtyProducts.addProductProperty 1, "something", "something" $scope.deleteVariant $scope.products[0], $scope.products[0].variants[0] expect($scope.products).toEqual([ - {id: 1, variants: []} + {id: 1, variants: [{id: -2}]} ]) expect(DirtyProducts.all()).toEqual 1: { id: 1, something: 'something'} - + describe "when the variant has been saved", -> it "deletes variants with a http delete request to /api/products/product_permalink/variants/(variant_id)/soft_delete", -> spyOn(window, "confirm").andReturn true @@ -800,9 +809,14 @@ describe "AdminProductEditCtrl", -> { id: 9 permalink_live: "apples" - variants: [ + variants: [{ id: 3 price: 12 + }, + { + id: 4 + price: 15 + } ] } { From 8248e382f34139b3cd51805c99c84058e66db784 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 3 Oct 2014 14:30:23 +1000 Subject: [PATCH 018/151] Greying out disabled action button --- .../stylesheets/admin/openfoodnetwork.css.scss | 15 +++++++++++++++ .../bulk_edit/_products_variant.html.haml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss index 641e6c5756..8518b47e50 100644 --- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss +++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss @@ -249,3 +249,18 @@ span.required { color: red; font-size: 110%; } + +table td.actions { + .icon-trash, .icon-edit, icon-copy { + &.disabled { + border-color: #d0d0d0; + color: #c0c0c0; + background-color: #fafafa; + &:hover { + border-color: #a5a5a5; + color: #a5a5a5; + background-color: #fafafa; + } + } + } +} diff --git a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml index 28f6ed6321..8825cec46c 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml @@ -20,4 +20,4 @@ %a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text", 'ng-show' => "variantSaved(variant)" } %td.actions %td.actions - %a{ 'ng-click' => 'deleteVariant(product,variant)', :class => "delete-variant icon-trash no-text" } + %a{ 'ng-click' => 'deleteVariant(product,variant)', "ng-class" => '{disabled: product.variants.length < 2}', :class => "delete-variant icon-trash no-text" } From 447a5481a38aadbc21f7d5eebe15e17aaf772694 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 3 Oct 2014 17:56:05 +1000 Subject: [PATCH 019/151] WIP: Building migration to duplicate master variants --- ...060622_add_standard_variant_to_products.rb | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 db/migrate/20141003060622_add_standard_variant_to_products.rb diff --git a/db/migrate/20141003060622_add_standard_variant_to_products.rb b/db/migrate/20141003060622_add_standard_variant_to_products.rb new file mode 100644 index 0000000000..1463784fba --- /dev/null +++ b/db/migrate/20141003060622_add_standard_variant_to_products.rb @@ -0,0 +1,49 @@ +class AddStandardVariantToProducts < ActiveRecord::Migration + def change + # Find products without any standard variants + products_with_only_master = Spree::Product.where( variants: [] ) + + + products_with_only_master.each do |product| + # Run the callback to add a copy of the master variant as a standard variant + product.send(:ensure_standard_variant) + + existing_master = product.master + new_variant = product.variants.first + + # Replace any existing references to the master variant with the new standard variant + + # Option Values + # Strategy: add all option values on existing_master to new_variant, and keep on existing_master + option_values = existing_master.option_values + option_values.each do |option_value| + variant_ids = option_value.variant_ids + variant_ids << new_variant.id + option_value.update_attributes(variant_ids: variant_ids) + end + + # Inventory Units + # Strategy: completely replace all references to existing_master with new_variant + inventory_units = existing_master.inventory_units + inventory_units.each do |inventory_unit| + inventory_unit.update_attributes(variant_id: new_variant.id ) + end + + # Line Items + # Strategy: completely replace all references to existing_master with new_variant + line_items = existing_master.line_items + line_items.each do |line_item| + line_item.update_attributes(variant_id: new_variant.id ) + end + + # Prices + # Strategy: duplicate all prices on existing_master and assign them to new_variant + prices = existing_master.prices + new_prices = [] + prices.each do |price| + new_prices << price.dup + end + new_variant.update_attributes(prices: new_prices) + end + end +end From 5d9e861ee428796b1b6881c28e697f041fd85b14 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 28 Jan 2015 17:13:56 +1100 Subject: [PATCH 020/151] Working migration to complete deprecation of master variants --- ...060622_add_standard_variant_to_products.rb | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/db/migrate/20141003060622_add_standard_variant_to_products.rb b/db/migrate/20141003060622_add_standard_variant_to_products.rb index 1463784fba..035cbff4d7 100644 --- a/db/migrate/20141003060622_add_standard_variant_to_products.rb +++ b/db/migrate/20141003060622_add_standard_variant_to_products.rb @@ -1,8 +1,7 @@ class AddStandardVariantToProducts < ActiveRecord::Migration - def change + def up # Find products without any standard variants - products_with_only_master = Spree::Product.where( variants: [] ) - + products_with_only_master = Spree::Product.find(:all, :include => "variants", :conditions => ["spree_variants.id IS NULL"]) products_with_only_master.each do |product| # Run the callback to add a copy of the master variant as a standard variant @@ -11,7 +10,15 @@ class AddStandardVariantToProducts < ActiveRecord::Migration existing_master = product.master new_variant = product.variants.first - # Replace any existing references to the master variant with the new standard variant + # Replace any relevant references to the master variant with the new standard variant + + # Inventory Units + # Strategy: do nothing to inventory units pertaining to existing_master, + # new inventory units will be created with reference to new_variant + + # Line Items + # Strategy: do nothing to line items pertaining to existing_master, + # new line items will be created with reference to new_variant # Option Values # Strategy: add all option values on existing_master to new_variant, and keep on existing_master @@ -22,28 +29,19 @@ class AddStandardVariantToProducts < ActiveRecord::Migration option_value.update_attributes(variant_ids: variant_ids) end - # Inventory Units - # Strategy: completely replace all references to existing_master with new_variant - inventory_units = existing_master.inventory_units - inventory_units.each do |inventory_unit| - inventory_unit.update_attributes(variant_id: new_variant.id ) - end - - # Line Items - # Strategy: completely replace all references to existing_master with new_variant - line_items = existing_master.line_items - line_items.each do |line_item| - line_item.update_attributes(variant_id: new_variant.id ) - end - # Prices # Strategy: duplicate all prices on existing_master and assign them to new_variant - prices = existing_master.prices - new_prices = [] - prices.each do |price| - new_prices << price.dup + existing_prices = existing_master.prices + existing_prices.each do |price| + new_variant.prices << price.dup + end + + # Exchange Variants + # Strategy: Replace all references to existing master in exchanges with new_variant + exchange_variants = ExchangeVariant.where(variant_id: existing_master.id) + exchange_variants.each do |exchange_variant| + exchange_variant.update_attributes(variant_id: new_variant.id ) end - new_variant.update_attributes(prices: new_prices) end end end From 7596270154f45a03bd90e418c9adabfcb2457d27 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 29 Jan 2015 12:37:31 +1100 Subject: [PATCH 021/151] A few more changes to tidy up standard variant migration --- .../20141003060622_add_standard_variant_to_products.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/db/migrate/20141003060622_add_standard_variant_to_products.rb b/db/migrate/20141003060622_add_standard_variant_to_products.rb index 035cbff4d7..7e7f752a5d 100644 --- a/db/migrate/20141003060622_add_standard_variant_to_products.rb +++ b/db/migrate/20141003060622_add_standard_variant_to_products.rb @@ -1,7 +1,7 @@ class AddStandardVariantToProducts < ActiveRecord::Migration def up # Find products without any standard variants - products_with_only_master = Spree::Product.find(:all, :include => "variants", :conditions => ["spree_variants.id IS NULL"]) + products_with_only_master = Spree::Product.includes(:variants).where('spree_variants.id IS NULL') products_with_only_master.each do |product| # Run the callback to add a copy of the master variant as a standard variant @@ -39,9 +39,7 @@ class AddStandardVariantToProducts < ActiveRecord::Migration # Exchange Variants # Strategy: Replace all references to existing master in exchanges with new_variant exchange_variants = ExchangeVariant.where(variant_id: existing_master.id) - exchange_variants.each do |exchange_variant| - exchange_variant.update_attributes(variant_id: new_variant.id ) - end + exchange_variants.update_all(variant_id: new_variant.id) end end end From 2d7fb3fd6749e1e27cda29640dc4fd7c6f3f45dd Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 29 Jan 2015 12:43:40 +1100 Subject: [PATCH 022/151] Updating name of spec --- spec/models/spree/product_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 35dd847d1f..ddc8f99757 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -61,7 +61,7 @@ module Spree end - it "does not allow only standard variant to be deleted" do + it "does not allow the last variant to be deleted" do product = create(:simple_product) expect(product.variants(:reload).length).to eq 1 product.variants = [] From 9ee25c4e424ac74cd5c852e8dcdcff7f09a66aab Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 29 Jan 2015 13:06:28 +1100 Subject: [PATCH 023/151] Making spec better --- spec/models/spree/product_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index ddc8f99757..42c85e98e3 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -120,9 +120,10 @@ module Spree expect(standard_variant.price).to eq product.master.price end - it "only duplicates master variant on initial save" do + it "only duplicates master variant on create" do + expect(product).to_not receive :ensure_standard_variant + product.name = "Something else" product.save! - expect(product.variants(:reload).length).to eq 1 end end From b9f19d5777ed5034297dd3725c17bccffd39cf5d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 17 Apr 2015 13:00:20 +1000 Subject: [PATCH 024/151] Fixing broken specs --- .../admin/variants_controller_decorator.rb | 2 +- app/models/spree/product_decorator.rb | 5 +- app/models/spree/product_set.rb | 2 +- app/models/spree/variant_decorator.rb | 18 +- app/views/shopping_shared/_contact.html.haml | 4 +- spec/controllers/cart_controller_spec.rb | 3 +- spec/controllers/shop_controller_spec.rb | 15 +- .../spree/admin/variants_controller_spec.rb | 10 +- spec/factories.rb | 2 +- .../admin/bulk_product_update_spec.rb | 292 ++++-------------- spec/features/admin/order_cycles_spec.rb | 42 +-- spec/features/admin/orders_spec.rb | 2 +- spec/features/admin/products_spec.rb | 5 +- spec/features/admin/reports_spec.rb | 26 +- spec/features/admin/variants_spec.rb | 27 +- .../consumer/shopping/shopping_spec.rb | 65 ++-- .../products_and_inventory_report_spec.rb | 45 ++- spec/models/enterprise_spec.rb | 33 +- spec/models/order_cycle_spec.rb | 18 +- spec/requests/shop_spec.rb | 73 ++--- 20 files changed, 237 insertions(+), 452 deletions(-) diff --git a/app/controllers/spree/admin/variants_controller_decorator.rb b/app/controllers/spree/admin/variants_controller_decorator.rb index acb55327ae..f83bf69eca 100644 --- a/app/controllers/spree/admin/variants_controller_decorator.rb +++ b/app/controllers/spree/admin/variants_controller_decorator.rb @@ -4,7 +4,7 @@ Spree::Admin::VariantsController.class_eval do def search search_params = { :product_name_cont => params[:q], :sku_cont => params[:q] } - @variants = Spree::Variant.ransack(search_params.merge(:m => 'or')).result + @variants = Spree::Variant.where(is_master: false).ransack(search_params.merge(:m => 'or')).result if params[:distributor_id].present? distributor = Enterprise.find params[:distributor_id] diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 3d35700b7c..f7834dc3ee 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -21,9 +21,7 @@ Spree::Product.class_eval do attr_accessible :supplier_id, :primary_taxon_id, :distributor_ids, :product_distributions_attributes, :group_buy, :group_buy_unit_size attr_accessible :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value, :unit_description, :notes, :images_attributes, :display_as - before_validation :ensure_standard_variant, if: :new_record? - - validates_presence_of :variants, message: "Product must have at least one variant" + # validates_presence_of :variants, unless: :new_record?, message: "Product must have at least one variant" validates_presence_of :supplier validates :primary_taxon, presence: { message: "^Product Category can't be blank" } validates :tax_category_id, presence: { message: "^Tax Category can't be blank" }, if: "Spree::Config.products_require_tax_category" @@ -34,6 +32,7 @@ Spree::Product.class_eval do validates_presence_of :variant_unit_name, if: -> p { p.variant_unit == 'items' } + after_save :ensure_standard_variant, if: lambda { master.valid? && variants.empty? } after_initialize :set_available_on_to_now, :if => :new_record? after_save :update_units after_touch :touch_distributors diff --git a/app/models/spree/product_set.rb b/app/models/spree/product_set.rb index 9b4b771f37..1a73ab00fe 100644 --- a/app/models/spree/product_set.rb +++ b/app/models/spree/product_set.rb @@ -14,7 +14,7 @@ class Spree::ProductSet < ModelSet if e.nil? @klass.new(attributes).save unless @reject_if.andand.call(attributes) else - e.update_attributes(attributes.except(:id, :variants_attributes, :master_attributes)) and + ( attributes.except(:id, :variants_attributes, :master_attributes).present? ? e.update_attributes(attributes.except(:id, :variants_attributes, :master_attributes)) : true) and (attributes[:variants_attributes] ? update_variants_attributes(e, attributes[:variants_attributes]) : true ) and (attributes[:master_attributes] ? update_variant(e, attributes[:master_attributes]) : true ) end diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index ee05b3b2b8..20c4f20cf7 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -18,7 +18,6 @@ Spree::Variant.class_eval do if: -> v { v.product.andand.variant_unit.present? && v.unit_value.nil? } before_validation :update_weight_from_unit_value, if: -> v { v.product.present? } - before_destroy :check_product_variants after_save :update_units scope :with_order_cycles_inner, joins(exchanges: :order_cycle) @@ -103,9 +102,13 @@ Spree::Variant.class_eval do end def delete - transaction do - self.update_column(:deleted_at, Time.now) - ExchangeVariant.where(variant_id: self).destroy_all + if product.variants == [self] # Only variant left on product + errors.add :product, "must have at least one variant" + else + transaction do + self.update_column(:deleted_at, Time.now) + ExchangeVariant.where(variant_id: self).destroy_all + end end end @@ -124,11 +127,4 @@ Spree::Variant.class_eval do option_value_namer.name end end - - def check_product_variants - if product.variants == [self] # Only variant left on product - errors.add :product, "must have at least one variant" - return false - end - end end diff --git a/app/views/shopping_shared/_contact.html.haml b/app/views/shopping_shared/_contact.html.haml index a5e7cddad9..6069d453a2 100644 --- a/app/views/shopping_shared/_contact.html.haml +++ b/app/views/shopping_shared/_contact.html.haml @@ -2,7 +2,7 @@ .panel .row .small-12.large-4.columns - - if current_distributor.address.address1 || current_distributor.address.address2 || current_distributor.address.city || current_distributor.address.state || current_distributor.address.zipcode + - if current_distributor.address.address1 || current_distributor.address.address2 || current_distributor.address.city || current_distributor.address.state || current_distributor.address.zipcode %div.center .header Address %strong=current_distributor.name @@ -49,7 +49,7 @@ - unless current_distributor.linkedin.blank? %span %a{href: "http://#{current_distributor.linkedin}", target: "_blank" } - %i.ofn-i_042-linkedin + %i.ofn-i_042-linkedin / = current_distributor.linkedin - unless current_distributor.instagram.blank? diff --git a/spec/controllers/cart_controller_spec.rb b/spec/controllers/cart_controller_spec.rb index db311a8104..4fda0d7824 100644 --- a/spec/controllers/cart_controller_spec.rb +++ b/spec/controllers/cart_controller_spec.rb @@ -75,8 +75,9 @@ module OpenFoodNetwork it 'should add variant to new order and return the order' do product1.distributors << distributor product1.save + variant = product1.variants.first - put :add_variant, { cart_id: cart, variant_id: product1.master.id, quantity: (product1.master.on_hand-1), distributor_id: distributor, order_cycle_id: nil, max_quantity: nil } + put :add_variant, { cart_id: cart, variant_id: variant.id, quantity: (variant.on_hand-1), distributor_id: distributor, order_cycle_id: nil, max_quantity: nil } cart.orders.size.should == 1 cart.orders.first.line_items.size.should == 1 diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index 4b7c53da19..3fdc1a7ea9 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -112,10 +112,10 @@ describe ShopController do let!(:p4) { create(:product, name: "jkl", primary_taxon_id: t1.id) } before do - exchange.variants << p1.master - exchange.variants << p2.master - exchange.variants << p3.master - exchange.variants << p4.master + exchange.variants << p1.variants.first + exchange.variants << p2.variants.first + exchange.variants << p3.variants.first + exchange.variants << p4.variants.first end it "sorts products by the distributor's preferred taxon list" do @@ -136,19 +136,20 @@ describe ShopController do context "RABL tests" do render_views let(:product) { create(:product) } + let(:variant) { product.variants.first } before do - exchange.variants << product.master + exchange.variants << variant controller.stub(:current_order_cycle).and_return order_cycle end + it "only returns products for the current order cycle" do xhr :get, :products response.body.should have_content product.name end it "doesn't return products not in stock" do - product.update_attribute(:on_demand, false) - product.master.update_attribute(:count_on_hand, 0) + variant.update_attribute(:count_on_hand, 0) xhr :get, :products response.body.should_not have_content product.name end diff --git a/spec/controllers/spree/admin/variants_controller_spec.rb b/spec/controllers/spree/admin/variants_controller_spec.rb index 9c6d77194d..a0e7918d7c 100644 --- a/spec/controllers/spree/admin/variants_controller_spec.rb +++ b/spec/controllers/spree/admin/variants_controller_spec.rb @@ -8,22 +8,24 @@ module Spree describe "search action" do let!(:p1) { create(:simple_product, name: 'Product 1') } let!(:p2) { create(:simple_product, name: 'Product 2') } + let!(:v1) { p1.variants.first } + let!(:v2) { p2.variants.first } let!(:d) { create(:distributor_enterprise) } - let!(:oc) { create(:simple_order_cycle, distributors: [d], variants: [p1.master]) } + let!(:oc) { create(:simple_order_cycle, distributors: [d], variants: [v1]) } it "filters by distributor" do spree_get :search, q: 'Prod', distributor_id: d.id.to_s - assigns(:variants).should == [p1.master] + assigns(:variants).should == [v1] end it "filters by order cycle" do spree_get :search, q: 'Prod', order_cycle_id: oc.id.to_s - assigns(:variants).should == [p1.master] + assigns(:variants).should == [v1] end it "does not filter when no distributor or order cycle is specified" do spree_get :search, q: 'Prod' - assigns(:variants).sort.should == [p1.master, p2.master].sort + assigns(:variants).sort.should == [v1,v2].sort end end end diff --git a/spec/factories.rb b/spec/factories.rb index 7effe49a9d..82db4c1520 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -45,7 +45,7 @@ FactoryGirl.define do image = File.open(File.expand_path('../../app/assets/images/logo.jpg', __FILE__)) Spree::Image.create({:viewable_id => product.master.id, :viewable_type => 'Spree::Variant', :alt => "position 1", :attachment => image, :position => 1}) - exchange.variants << product.master + exchange.variants << product.variants.first end variants = [ex1, ex2].map(&:variants).flatten diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index debb234284..f5cc856da2 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -53,62 +53,18 @@ feature %q{ expect(page).to have_field "available_on", with: p2.available_on.strftime("%F %T") end - it "displays a price input for each product without variants (ie. for master variant)" do - p1 = FactoryGirl.create(:product) - p2 = FactoryGirl.create(:product) - p3 = FactoryGirl.create(:product) - v = FactoryGirl.create(:variant, product: p3) - - p1.update_attribute :price, 22.0 - p2.update_attribute :price, 44.0 - p3.update_attribute :price, 66.0 + it "displays an on hand count in a span for each product" do + p1 = FactoryGirl.create(:product, on_hand: 15) + v1 = p1.variants.first + v1.on_hand = 4 + v1.save! visit '/admin/products/bulk_edit' - expect(page).to have_field "price", with: "22.0" - expect(page).to have_field "price", with: "44.0" - expect(page).to have_no_field "price", with: "66.0", visible: true - end - - it "displays an on hand count input for each product (ie. for master variant) if no regular variants exist" do - p1 = FactoryGirl.create(:product) - p2 = FactoryGirl.create(:product) - p1.on_hand = 15 - p2.on_hand = 12 - p1.save! - p2.save! - - visit '/admin/products/bulk_edit' - - expect(page).to have_no_selector "span[name='on_hand']", text: "0" - expect(page).to have_field "on_hand", with: "15" - expect(page).to have_field "on_hand", with: "12" - end - - it "displays an on hand count in a span for each product (ie. for master variant) if other variants exist" do - p1 = FactoryGirl.create(:product) - p2 = FactoryGirl.create(:product) - v1 = FactoryGirl.create(:variant, product: p1, is_master: false, on_hand: 4) - p1.on_hand = 15 - p2.on_hand = 12 - p1.save! - p2.save! - - visit '/admin/products/bulk_edit' - - expect(page).to have_no_field "on_hand", with: "15" - expect(page).to have_selector "span[name='on_hand']", text: "4" - expect(page).to have_field "on_hand", with: "12" - end - - it "displays 'on demand' for the on hand count when the product is available on demand" do - p1 = FactoryGirl.create(:product, on_demand: true) - p1.master.on_demand = true; p1.master.save! - - visit '/admin/products/bulk_edit' - - expect(page).to have_selector "span[name='on_hand']", text: "On demand" - expect(page).to have_no_field "on_hand", visible: true + within "#p_#{p1.id}" do + expect(page).to have_no_field "on_hand", with: "15" + expect(page).to have_selector "span[name='on_hand']", text: "4" + end end it "displays 'on demand' for any variant that is available on demand" do @@ -170,7 +126,7 @@ feature %q{ visit '/admin/products/bulk_edit' all("a.view-variants").each { |e| e.trigger('click') } - expect(page).to have_selector "span[name='on_hand']", text: "21" + expect(page).to have_selector "span[name='on_hand']", text: p1.variants.sum{ |v| v.on_hand }.to_s expect(page).to have_field "variant_on_hand", with: "15" expect(page).to have_field "variant_on_hand", with: "6" end @@ -218,7 +174,9 @@ feature %q{ expect(page).to have_content 'NEW PRODUCT' fill_in 'product_name', :with => 'Big Bag Of Apples' - select(s.name, :from => 'product_supplier_id') + select s.name, :from => 'product_supplier_id' + select 'Weight (g)', from: 'product_variant_unit_with_scale' + fill_in 'product_unit_value_with_description', with: '100' fill_in 'product_price', :with => '10.00' select taxon.name, from: 'product_primary_taxon_id' click_button 'Create' @@ -231,34 +189,25 @@ feature %q{ scenario "creating new variants" do # Given a product without variants or a unit - p = FactoryGirl.create(:product, variant_unit: nil, variant_unit_scale: nil) + p = FactoryGirl.create(:product, variant_unit: 'weight', variant_unit_scale: 1000) login_to_admin_section visit '/admin/products/bulk_edit' - # I should not see an add variant button - expect(page).to have_no_selector 'a.add-variant', visible: true - - # When I set the unit - select "Weight (kg)", from: "variant_unit_with_scale" - # I should see an add variant button - expect(page).to have_selector 'a.add-variant', visible: true + page.find('a.view-variants').trigger('click') # When I add three variants page.find('a.add-variant', visible: true).trigger('click') page.find('a.add-variant', visible: true).trigger('click') - page.find('a.add-variant', visible: true).trigger('click') - # They should be added, and should see no edit buttons - variant_count = page.all("tr.variant").count - expect(variant_count).to eq 3 - expect(page).to have_no_selector "a.edit-variant", visible: true + # They should be added, and should not see edit buttons for new variants + expect(page).to have_selector "tr.variant", count: 3 + expect(page).to have_selector "a.edit-variant", count: 1 # When I remove two, they should be removed page.all('a.delete-variant').first.click page.all('a.delete-variant').first.click - variant_count = page.all("tr.variant").count - expect(variant_count).to eq 1 + expect(page).to have_selector "tr.variant", count: 1 # When I fill out variant details and hit update fill_in "variant_display_name", with: "Case of 12 Bottles" @@ -266,6 +215,7 @@ feature %q{ fill_in "variant_display_as", with: "Case" fill_in "variant_price", with: "4.0" fill_in "variant_on_hand", with: "10" + click_button 'Save Changes' expect(page.find("#status-message")).to have_content "Changes saved." @@ -281,59 +231,6 @@ feature %q{ expect(page).to have_selector "a.edit-variant", visible: true end - - scenario "updating a product with no variants (except master)" do - s1 = FactoryGirl.create(:supplier_enterprise) - s2 = FactoryGirl.create(:supplier_enterprise) - t1 = FactoryGirl.create(:taxon) - t2 = FactoryGirl.create(:taxon) - p = FactoryGirl.create(:product, supplier: s1, available_on: Date.today, variant_unit: 'volume', variant_unit_scale: 1, primary_taxon: t2) - p.price = 10.0 - p.on_hand = 6; - p.save! - - login_to_admin_section - - visit '/admin/products/bulk_edit' - - first("div#columns_dropdown", :text => "COLUMNS").click - first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click - first("div#columns_dropdown div.menu div.menu_item", text: "Category").click - - within "tr#p_#{p.id}" do - expect(page).to have_field "product_name", with: p.name - expect(page).to have_select "producer_id", selected: s1.name - expect(page).to have_field "available_on", with: p.available_on.strftime("%F %T") - expect(page).to have_field "price", with: "10.0" - expect(page).to have_selector "div#s2id_p#{p.id}_category_id a.select2-choice" - expect(page).to have_select "variant_unit_with_scale", selected: "Volume (L)" - expect(page).to have_field "on_hand", with: "6" - - fill_in "product_name", with: "Big Bag Of Potatoes" - select s2.name, :from => 'producer_id' - fill_in "available_on", with: (3.days.ago.beginning_of_day).strftime("%F %T") - fill_in "price", with: "20" - select "Weight (kg)", from: "variant_unit_with_scale" - select2_select t1.name, from: "p#{p.id}_category_id" - fill_in "on_hand", with: "18" - fill_in "display_as", with: "Big Bag" - end - - click_button 'Save Changes' - expect(page.find("#status-message")).to have_content "Changes saved." - - p.reload - expect(p.name).to eq "Big Bag Of Potatoes" - expect(p.supplier).to eq s2 - expect(p.variant_unit).to eq "weight" - expect(p.variant_unit_scale).to eq 1000 # Kg - expect(p.available_on).to eq 3.days.ago.beginning_of_day - expect(p.master.display_as).to eq "Big Bag" - expect(p.price).to eq 20.0 - expect(p.on_hand).to eq 18 - expect(p.primary_taxon).to eq t1 - end - scenario "updating a product with a variant unit of 'items'" do p = FactoryGirl.create(:product, variant_unit: 'weight', variant_unit_scale: 1000) @@ -355,74 +252,12 @@ feature %q{ expect(p.variant_unit_name).to eq "loaf" end - scenario "setting a variant unit on a product that has none" do - p = FactoryGirl.create(:product, variant_unit: nil, variant_unit_scale: nil) - v = FactoryGirl.create(:variant, product: p, unit_value: nil, unit_description: nil) - - login_to_admin_section - - visit '/admin/products/bulk_edit' - - expect(page).to have_select "variant_unit_with_scale", selected: '' - - select "Weight (kg)", from: "variant_unit_with_scale" - first("a.view-variants").trigger('click') - fill_in "variant_unit_value_with_description", with: '123 abc' - - click_button 'Save Changes' - expect(page.find("#status-message")).to have_content "Changes saved." - - p.reload - expect(p.variant_unit).to eq "weight" - expect(p.variant_unit_scale).to eq 1000 # Kg - v.reload - expect(v.unit_value).to eq 123000 # 123 kg in g - expect(v.unit_description).to eq "abc" - end - - describe "setting the master unit value for a product without variants" do - it "sets the master unit value" do - p = FactoryGirl.create(:product, variant_unit: nil, variant_unit_scale: nil) - - login_to_admin_section - - visit '/admin/products/bulk_edit' - - expect(page).to have_select "variant_unit_with_scale", selected: '' - expect(page).to have_no_field "master_unit_value_with_description", visible: true - - select "Weight (kg)", from: "variant_unit_with_scale" - fill_in "master_unit_value_with_description", with: '123 abc' - - click_button 'Save Changes' - expect(page.find("#status-message")).to have_content "Changes saved." - - p.reload - expect(p.variant_unit).to eq 'weight' - expect(p.variant_unit_scale).to eq 1000 - expect(p.master.unit_value).to eq 123000 - expect(p.master.unit_description).to eq 'abc' - end - - it "does not show the field when the product has variants" do - p = FactoryGirl.create(:product, variant_unit: nil, variant_unit_scale: nil) - v = FactoryGirl.create(:variant, product: p, unit_value: nil, unit_description: nil) - - login_to_admin_section - - visit '/admin/products/bulk_edit' - - select "Weight (kg)", from: "variant_unit_with_scale" - expect(page).to have_no_field "master_unit_value_with_description", visible: true - end - end - - scenario "updating a product with variants" do s1 = FactoryGirl.create(:supplier_enterprise) s2 = FactoryGirl.create(:supplier_enterprise) - p = FactoryGirl.create(:product, supplier: s1, available_on: Date.today, variant_unit: 'volume', variant_unit_scale: 0.001) - v = FactoryGirl.create(:variant, product: p, price: 3.0, on_hand: 9, unit_value: 0.25, unit_description: '(bottle)') + p = FactoryGirl.create(:product, supplier: s1, available_on: Date.today, variant_unit: 'volume', variant_unit_scale: 0.001, + price: 3.0, on_hand: 9, unit_value: 0.25, unit_description: '(bottle)' ) + v = p.variants.first login_to_admin_section @@ -433,7 +268,7 @@ feature %q{ expect(page).to have_field "variant_price", with: "3.0" expect(page).to have_field "variant_unit_value_with_description", with: "250 (bottle)" expect(page).to have_field "variant_on_hand", with: "9" - expect(page).to have_selector "span[name='on_hand']", text: "9" + expect(page).to have_selector "span[name='on_hand']", "9" select "Volume (L)", from: "variant_unit_with_scale" fill_in "variant_price", with: "4.0" @@ -464,7 +299,9 @@ feature %q{ expect(page).to have_field "variant_price", with: "3.0" - fill_in "variant_price", with: "10.0" + within "#v_#{v.id}" do + fill_in "variant_price", with: "10.0" + end click_button 'Save Changes' expect(page.find("#status-message")).to have_content "Changes saved." @@ -553,40 +390,39 @@ feature %q{ describe "using action buttons" do describe "using delete buttons" do - it "shows a delete button for products, which deletes the appropriate product when clicked" do - p1 = FactoryGirl.create(:product) - p2 = FactoryGirl.create(:product) - p3 = FactoryGirl.create(:product) - login_to_admin_section + let!(:p1) { FactoryGirl.create(:product) } + let!(:p2) { FactoryGirl.create(:product) } + let!(:v1) { p1.variants.first } + let!(:v2) { p2.variants.first } + let!(:v3) { FactoryGirl.create(:variant, product: p2 ) } + + before do + quick_login_as_admin visit '/admin/products/bulk_edit' + end - expect(page).to have_selector "a.delete-product", :count => 3 + it "shows a delete button for products, which deletes the appropriate product when clicked" do + expect(page).to have_selector "a.delete-product", :count => 2 within "tr#p_#{p1.id}" do first("a.delete-product").click end - expect(page).to have_selector "a.delete-product", :count => 2 + expect(page).to have_selector "a.delete-product", :count => 1 visit '/admin/products/bulk_edit' - expect(page).to have_selector "a.delete-product", :count => 2 + expect(page).to have_selector "a.delete-product", :count => 1 end it "shows a delete button for variants, which deletes the appropriate variant when clicked" do - v1 = FactoryGirl.create(:variant) - v2 = FactoryGirl.create(:variant) - v3 = FactoryGirl.create(:variant) - login_to_admin_section - - visit '/admin/products/bulk_edit' expect(page).to have_selector "a.view-variants" all("a.view-variants").each { |e| e.trigger('click') } expect(page).to have_selector "a.delete-variant", :count => 3 - within "tr#v_#{v1.id}" do + within "tr#v_#{v3.id}" do first("a.delete-variant").click end @@ -601,15 +437,18 @@ feature %q{ end describe "using edit buttons" do - it "shows an edit button for products, which takes the user to the standard edit page for that product" do - p1 = FactoryGirl.create(:product) - p2 = FactoryGirl.create(:product) - p3 = FactoryGirl.create(:product) - login_to_admin_section + let!(:p1) { FactoryGirl.create(:product) } + let!(:p2) { FactoryGirl.create(:product) } + let!(:v1) { p1.variants.first } + let!(:v2) { p2.variants.first } + before do + quick_login_as_admin visit '/admin/products/bulk_edit' + end - expect(page).to have_selector "a.edit-product", :count => 3 + it "shows an edit button for products, which takes the user to the standard edit page for that product" do + expect(page).to have_selector "a.edit-product", :count => 2 within "tr#p_#{p1.id}" do first("a.edit-product").click @@ -619,16 +458,10 @@ feature %q{ end it "shows an edit button for variants, which takes the user to the standard edit page for that variant" do - v1 = FactoryGirl.create(:variant) - v2 = FactoryGirl.create(:variant) - v3 = FactoryGirl.create(:variant) - login_to_admin_section - - visit '/admin/products/bulk_edit' expect(page).to have_selector "a.view-variants" all("a.view-variants").each { |e| e.trigger('click') } - expect(page).to have_selector "a.edit-variant", :count => 3 + expect(page).to have_selector "a.edit-variant", :count => 2 within "tr#v_#{v1.id}" do first("a.edit-variant").click @@ -788,6 +621,8 @@ feature %q{ within 'fieldset#new_product' do fill_in 'product_name', with: 'Big Bag Of Apples' select supplier_permitted.name, from: 'product_supplier_id' + select 'Weight (g)', from: 'product_variant_unit_with_scale' + fill_in 'product_unit_value_with_description', with: '100' fill_in 'product_price', with: '10.00' select taxon.name, from: 'product_primary_taxon_id' end @@ -800,6 +635,7 @@ feature %q{ it "allows me to update a product" do p = product_supplied_permitted + v = p.variants.first visit '/admin/products/bulk_edit' first("div#columns_dropdown", :text => "COLUMNS").click @@ -809,30 +645,34 @@ feature %q{ expect(page).to have_field "product_name", with: p.name expect(page).to have_select "producer_id", selected: supplier_permitted.name expect(page).to have_field "available_on", with: p.available_on.strftime("%F %T") - expect(page).to have_field "price", with: "10.0" - expect(page).to have_field "on_hand", with: "6" fill_in "product_name", with: "Big Bag Of Potatoes" select supplier_managed2.name, :from => 'producer_id' fill_in "available_on", with: (3.days.ago.beginning_of_day).strftime("%F %T") - fill_in "price", with: "20" select "Weight (kg)", from: "variant_unit_with_scale" - fill_in "on_hand", with: "18" - fill_in "display_as", with: "Big Bag" + + find("a.view-variants").trigger('click') + end + + within "#v_#{v.id}" do + fill_in "variant_price", with: "20" + fill_in "variant_on_hand", with: "18" + fill_in "variant_display_as", with: "Big Bag" end click_button 'Save Changes' expect(page.find("#status-message")).to have_content "Changes saved." p.reload + v.reload expect(p.name).to eq "Big Bag Of Potatoes" expect(p.supplier).to eq supplier_managed2 expect(p.variant_unit).to eq "weight" expect(p.variant_unit_scale).to eq 1000 # Kg expect(p.available_on).to eq 3.days.ago.beginning_of_day - expect(p.master.display_as).to eq "Big Bag" - expect(p.price).to eq 20.0 - expect(p.on_hand).to eq 18 + expect(v.display_as).to eq "Big Bag" + expect(v.price).to eq 20.0 + expect(v.on_hand).to eq 18 end end end diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index cb96799fbb..bbf9fb7b36 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -487,7 +487,9 @@ feature %q{ let!(:shipping_method) { create(:shipping_method, distributors: [distributor_managed, distributor_unmanaged, distributor_permitted]) } let!(:payment_method) { create(:payment_method, distributors: [distributor_managed, distributor_unmanaged, distributor_permitted]) } let!(:product_managed) { create(:product, supplier: supplier_managed) } + let!(:variant_managed) { product_managed.variants.first } let!(:product_permitted) { create(:product, supplier: supplier_permitted) } + let!(:variant_permitted) { product_permitted.variants.first } before do # Relationships required for interface to work @@ -533,8 +535,6 @@ feature %q{ click_link "Order Cycles" click_link 'New Order Cycle' - # We go straight through to the new form, because only one coordinator is available - fill_in 'order_cycle_name', with: 'My order cycle' fill_in 'order_cycle_orders_open_at', with: '2040-11-06 06:00:00' fill_in 'order_cycle_orders_close_at', with: '2040-11-13 17:00:00' @@ -544,8 +544,8 @@ feature %q{ select 'Permitted supplier', from: 'new_supplier_id' click_button 'Add supplier' - select_incoming_variant supplier_managed, 0, product_managed.master - select_incoming_variant supplier_permitted, 1, product_permitted.master + select_incoming_variant supplier_managed, 0, variant_managed + select_incoming_variant supplier_permitted, 1, variant_permitted click_button 'Add coordinator fee' select 'Managed distributor fee', from: 'order_cycle_coordinator_fee_0_id' @@ -750,7 +750,9 @@ feature %q{ let!(:p1) { create(:simple_product, supplier: enterprise) } let!(:p2) { create(:simple_product, supplier: enterprise) } let!(:p3) { create(:simple_product, supplier: enterprise) } - let!(:v) { create(:variant, product: p3) } + let!(:v1) { p1.variants.first } + let!(:v2) { p2.variants.first } + let!(:v3) { p3.variants.first } let!(:fee) { create(:enterprise_fee, enterprise: enterprise, name: 'Coord fee') } before do @@ -779,12 +781,12 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'pickup instructions' # Then my products / variants should already be selected - page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{p1.master.id}" - page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{p2.master.id}" - page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{v.id}" + page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{v1.id}" + page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{v2.id}" + page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{v3.id}" # When I unselect a product - uncheck "order_cycle_incoming_exchange_0_variants_#{p2.master.id}" + uncheck "order_cycle_incoming_exchange_0_variants_#{v2.id}" # And I add a fee and save click_button 'Add coordinator fee' @@ -819,7 +821,7 @@ feature %q{ scenario "editing an order cycle" do # Given an order cycle with pickup time and instructions fee = create(:enterprise_fee, name: 'my fee', enterprise: enterprise) - oc = create(:simple_order_cycle, suppliers: [enterprise], coordinator: enterprise, distributors: [enterprise], variants: [p1.master], coordinator_fees: [fee]) + oc = create(:simple_order_cycle, suppliers: [enterprise], coordinator: enterprise, distributors: [enterprise], variants: [v1], coordinator_fees: [fee]) ex = oc.exchanges.outgoing.first ex.update_attributes! pickup_time: 'pickup time', pickup_instructions: 'pickup instructions' @@ -837,9 +839,9 @@ feature %q{ page.should have_field 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'pickup instructions' # And I should see the products - page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{p1.master.id}" - page.should have_unchecked_field "order_cycle_incoming_exchange_0_variants_#{p2.master.id}" - page.should have_unchecked_field "order_cycle_incoming_exchange_0_variants_#{v.id}" + page.should have_checked_field "order_cycle_incoming_exchange_0_variants_#{v1.id}" + page.should have_unchecked_field "order_cycle_incoming_exchange_0_variants_#{v2.id}" + page.should have_unchecked_field "order_cycle_incoming_exchange_0_variants_#{v3.id}" # And I should see the coordinator fees page.should have_select 'order_cycle_coordinator_fee_0_id', selected: 'my fee' @@ -849,7 +851,7 @@ feature %q{ # Given an order cycle with pickup time and instructions fee1 = create(:enterprise_fee, name: 'my fee', enterprise: enterprise) fee2 = create(:enterprise_fee, name: 'that fee', enterprise: enterprise) - oc = create(:simple_order_cycle, suppliers: [enterprise], coordinator: enterprise, distributors: [enterprise], variants: [p1.master], coordinator_fees: [fee1]) + oc = create(:simple_order_cycle, suppliers: [enterprise], coordinator: enterprise, distributors: [enterprise], variants: [v1], coordinator_fees: [fee1]) ex = oc.exchanges.outgoing.first ex.update_attributes! pickup_time: 'pickup time', pickup_instructions: 'pickup instructions' @@ -866,10 +868,10 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'zzy' # And I make some product selections - uncheck "order_cycle_incoming_exchange_0_variants_#{p1.master.id}" - check "order_cycle_incoming_exchange_0_variants_#{p2.master.id}" - check "order_cycle_incoming_exchange_0_variants_#{v.id}" - uncheck "order_cycle_incoming_exchange_0_variants_#{v.id}" + uncheck "order_cycle_incoming_exchange_0_variants_#{v1.id}" + check "order_cycle_incoming_exchange_0_variants_#{v2.id}" + check "order_cycle_incoming_exchange_0_variants_#{v3.id}" + uncheck "order_cycle_incoming_exchange_0_variants_#{v3.id}" # And I select some fees and update click_link 'order_cycle_coordinator_fee_0_remove' @@ -887,8 +889,8 @@ feature %q{ # And it should have a variant selected oc = OrderCycle.last - oc.exchanges.incoming.first.variants.should == [p2.master] - oc.exchanges.outgoing.first.variants.should == [p2.master] + oc.exchanges.incoming.first.variants.should == [v2] + oc.exchanges.outgoing.first.variants.should == [v2] # And it should have the fee oc.coordinator_fees.should == [fee2] diff --git a/spec/features/admin/orders_spec.rb b/spec/features/admin/orders_spec.rb index bb38e5d8ba..c2a302fcbb 100644 --- a/spec/features/admin/orders_spec.rb +++ b/spec/features/admin/orders_spec.rb @@ -11,7 +11,7 @@ feature %q{ @user = create(:user) @product = create(:simple_product) @distributor = create(:distributor_enterprise) - @order_cycle = create(:simple_order_cycle, distributors: [@distributor], variants: [@product.master]) + @order_cycle = create(:simple_order_cycle, distributors: [@distributor], variants: [@product.variants.first]) @order = create(:order_with_totals_and_distribution, user: @user, distributor: @distributor, order_cycle: @order_cycle, state: 'complete', payment_state: 'balance_due') diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index 740de12045..269c77d801 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -117,9 +117,8 @@ feature %q{ end end - scenario "creating a new product" do + scenario "creating a new product", js: true do Spree::Config.products_require_tax_category = false - click_link 'Products' click_link 'New Product' @@ -128,6 +127,8 @@ feature %q{ page.should have_selector('#product_supplier_id') select 'Another Supplier', :from => 'product_supplier_id' + select 'Weight (g)', from: 'product_variant_unit_with_scale' + fill_in 'product_unit_value_with_description', with: '500' select taxon.name, from: "product_primary_taxon_id" select 'None', from: "product_tax_category_id" diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index f16b736c00..277f33593b 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -223,14 +223,16 @@ feature %q{ describe "products and inventory report" do it "shows products and inventory report" do - product_1 = create(:simple_product, name: "Product Name", variant_unit: nil) - variant_1 = create(:variant, product: product_1, price: 100.0) - variant_2 = create(:variant, product: product_1, price: 80.0) - product_2 = create(:simple_product, name: "Product 2", price: 99.0, variant_unit: nil) - variant_1.update_column(:count_on_hand, 10) - variant_2.update_column(:count_on_hand, 20) - product_2.master.update_column(:count_on_hand, 9) - variant_1.option_values = [create(:option_value, :presentation => "Test")] + product1 = create(:simple_product, name: "Product Name", price: 100) + variant1 = product1.variants.first + variant2 = create(:variant, product: product1, price: 80.0) + product2 = create(:simple_product, name: "Product 2", price: 99.0, variant_unit: 'weight', variant_unit_scale: 1, unit_value: '100') + variant3 = product2.variants.first + variant1.update_column(:count_on_hand, 10) + variant2.update_column(:count_on_hand, 20) + variant3.update_column(:count_on_hand, 9) + variant1.option_values = [create(:option_value, :presentation => "Test")] + variant2.option_values = [create(:option_value, :presentation => "Something")] login_to_admin_section click_link 'Reports' @@ -244,10 +246,10 @@ feature %q{ table = rows.map { |r| r.all("th,td").map { |c| c.text.strip } } table.sort.should == [ - ["Supplier", "Producer Suburb", "Product", "Product Properties", "Taxons", "Variant Value", "Price", "Group Buy Unit Quantity", "Amount"], - [product_1.supplier.name, product_1.supplier.address.city, "Product Name", product_1.properties.join(", "), product_1.primary_taxon.name, "Test", "100.0", product_1.group_buy_unit_size.to_s, ""], - [product_1.supplier.name, product_1.supplier.address.city, "Product Name", product_1.properties.join(", "), product_1.primary_taxon.name, "S", "80.0", product_1.group_buy_unit_size.to_s, ""], - [product_2.supplier.name, product_1.supplier.address.city, "Product 2", product_1.properties.join(", "), product_2.primary_taxon.name, "", "99.0", product_1.group_buy_unit_size.to_s, ""] + ["Supplier", "Producer Suburb", "Product", "Product Properties", "Taxons", "Variant Value", "Price", "Group Buy Unit Quantity", "Amount"], + [product1.supplier.name, product1.supplier.address.city, "Product Name", product1.properties.join(", "), product1.primary_taxon.name, "Test", "100.0", product1.group_buy_unit_size.to_s, ""], + [product1.supplier.name, product1.supplier.address.city, "Product Name", product1.properties.join(", "), product1.primary_taxon.name, "Something", "80.0", product1.group_buy_unit_size.to_s, ""], + [product2.supplier.name, product1.supplier.address.city, "Product 2", product1.properties.join(", "), product2.primary_taxon.name, "100g", "99.0", product1.group_buy_unit_size.to_s, ""] ].sort end end diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb index 19767d8ea2..c947872949 100644 --- a/spec/features/admin/variants_spec.rb +++ b/spec/features/admin/variants_spec.rb @@ -25,10 +25,11 @@ feature %q{ end - scenario "editing unit value and description for a variant" do + scenario "editing unit value and description for a variant", js:true do # Given a product with unit-related option types, with a variant p = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1") - v = create(:variant, product: p, unit_value: 1, unit_description: 'foo') + v = p.variants.first + v.update_attributes( unit_value: 1, unit_description: 'foo' ) # And the product has option types for the unit-related and non-unit-related option values p.option_types << v.option_values.first.option_type @@ -39,7 +40,7 @@ feature %q{ page.find('table.index .icon-edit').click # Then I should not see a traditional option value field for the unit-related option value - page.all("div[data-hook='presentation'] input").count.should == 1 + expect(page).to_not have_selector "div[data-hook='presentation'] input" # And I should see unit value and description fields for the unit-related option value page.should have_field "variant_unit_value", with: "1" @@ -57,27 +58,9 @@ feature %q{ v.unit_description.should == 'bar' end - it "does not show unit value or description fields when the product does not have a unit-related option type" do - # Given a product without unit-related option types, with a variant - p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil) - v = create(:variant, product: p, unit_value: nil, unit_description: nil) - - # And the product has option types for the variant's option values - p.option_types << v.option_values.first.option_type - - # When I view the variant - login_to_admin_section - visit spree.admin_product_variants_path p - page.find('table.index .icon-edit').click - - # Then I should not see unit value and description fields - page.should_not have_field "variant_unit_value" - page.should_not have_field "variant_unit_description" - end - it "soft-deletes variants", js: true do p = create(:simple_product) - v = create(:variant, product: p) + v = p.variants.first login_to_admin_section visit spree.admin_product_variants_path p diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index a47b73ba6c..e11664c457 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -13,9 +13,10 @@ feature "As a consumer I want to shop with a distributor", js: true do let(:oc1) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), orders_close_at: 2.days.from_now) } let(:oc2) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), orders_close_at: 3.days.from_now) } let(:product) { create(:simple_product, supplier: supplier) } + let(:variant) { product.variants.first } let(:order) { create(:order, distributor: distributor) } - before do + before do set_order order end @@ -28,23 +29,23 @@ feature "As a consumer I want to shop with a distributor", js: true do visit shop_path page.should have_text distributor.name find("#tab_about a").click - first("distributor img")['src'].should == distributor.logo.url(:thumb) + first("distributor img")['src'].should == distributor.logo.url(:thumb) end it "shows the producers for a distributor" do - exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) - exchange.variants << product.master + exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) + exchange.variants << variant visit shop_path find("#tab_producers a").click - page.should have_content supplier.name + page.should have_content supplier.name end describe "selecting an order cycle" do let(:exchange1) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } it "selects an order cycle if only one is open" do - exchange1.update_attribute :pickup_time, "turtles" + exchange1.update_attribute :pickup_time, "turtles" visit shop_path page.should have_selector "option[selected]", text: 'turtles' end @@ -52,8 +53,8 @@ feature "As a consumer I want to shop with a distributor", js: true do describe "with multiple order cycles" do let(:exchange2) { Exchange.find(oc2.exchanges.to_enterprises(distributor).outgoing.first.id) } before do - exchange1.update_attribute :pickup_time, "frogs" - exchange2.update_attribute :pickup_time, "turtles" + exchange1.update_attribute :pickup_time, "frogs" + exchange2.update_attribute :pickup_time, "turtles" end it "shows a select with all order cycles, but doesn't show the products by default" do @@ -62,22 +63,22 @@ feature "As a consumer I want to shop with a distributor", js: true do page.should have_selector "option", text: 'turtles' page.should_not have_selector("input.button.right", visible: true) end - + it "shows products after selecting an order cycle" do - product.master.update_attribute(:display_name, "kitten") - product.master.update_attribute(:display_as, "rabbit") - exchange1.variants << product.master ## add product to exchange + variant.update_attribute(:display_name, "kitten") + variant.update_attribute(:display_as, "rabbit") + exchange1.variants << variant ## add product to exchange visit shop_path - page.should_not have_content product.name + page.should_not have_content product.name Spree::Order.last.order_cycle.should == nil select "frogs", :from => "order_cycle_id" page.should have_selector "products" - page.should have_content "Next order closing in 2 days" + page.should have_content "Next order closing in 2 days" Spree::Order.last.order_cycle.should == oc1 - page.should have_content product.name - page.should have_content product.master.display_name - page.should have_content product.master.display_as + page.should have_content product.name + page.should have_content variant.display_name + page.should have_content variant.display_as open_product_modal product modal_should_be_open_for product @@ -88,21 +89,16 @@ feature "As a consumer I want to shop with a distributor", js: true do describe "after selecting an order cycle with products visible" do let(:variant1) { create(:variant, product: product, price: 20) } let(:variant2) { create(:variant, product: product, price: 30) } - let(:exchange) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } + let(:exchange) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } before do - exchange.update_attribute :pickup_time, "frogs" - exchange.variants << product.master + exchange.update_attribute :pickup_time, "frogs" + exchange.variants << variant exchange.variants << variant1 exchange.variants << variant2 order.order_cycle = oc1 end - it "should not show quantity field for product with variants" do - visit shop_path - page.should_not have_selector("#variants_#{product.master.id}", visible: true) - end - it "uses the adjusted price" do enterprise_fee1 = create(:enterprise_fee, amount: 20) enterprise_fee2 = create(:enterprise_fee, amount: 3) @@ -126,6 +122,7 @@ feature "As a consumer I want to shop with a distributor", js: true do describe "group buy products" do let(:exchange) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } let(:product) { create(:simple_product, group_buy: true, on_hand: 15) } + let(:variant) { product.variants.first } let(:product2) { create(:simple_product, group_buy: false) } describe "without variants" do @@ -134,22 +131,11 @@ feature "As a consumer I want to shop with a distributor", js: true do set_order_cycle(order, oc1) visit shop_path end - - it "should save group buy data to ze cart" do - fill_in "variants[#{product.master.id}]", with: 5 - fill_in "variant_attributes[#{product.master.id}][max_quantity]", with: 9 - wait_until { !cart_dirty } - - li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last - li.max_quantity.should == 9 - li.quantity.should == 5 - end - # TODO move to controller test pending "adding a product with a max quantity less than quantity results in max_quantity==quantity" do - fill_in "variants[#{product.master.id}]", with: 5 - fill_in "variant_attributes[#{product.master.id}][max_quantity]", with: 1 + fill_in "variants[#{variant.id}]", with: 5 + fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 1 add_to_cart page.should have_content product.name li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last @@ -165,7 +151,7 @@ feature "As a consumer I want to shop with a distributor", js: true do set_order_cycle(order, oc1) visit shop_path end - + it "should save group buy data to the cart" do fill_in "variants[#{variant.id}]", with: 6 fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 7 @@ -213,4 +199,3 @@ feature "As a consumer I want to shop with a distributor", js: true do end end end - diff --git a/spec/lib/open_food_network/products_and_inventory_report_spec.rb b/spec/lib/open_food_network/products_and_inventory_report_spec.rb index 2e23919fe6..3cde6e5f8e 100644 --- a/spec/lib/open_food_network/products_and_inventory_report_spec.rb +++ b/spec/lib/open_food_network/products_and_inventory_report_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' module OpenFoodNetwork describe ProductsAndInventoryReport do context "As a site admin" do - let(:user) do + let(:user) do user = create(:user) user.spree_roles << Spree::Role.find_or_create_by_name!("admin") user @@ -14,7 +14,7 @@ module OpenFoodNetwork it "Should return headers" do subject.header.should == [ - "Supplier", + "Supplier", "Producer Suburb", "Product", "Product Properties", @@ -63,7 +63,7 @@ module OpenFoodNetwork context "As an enterprise user" do let(:supplier) { create(:supplier_enterprise) } - let(:enterprise_user) do + let(:enterprise_user) do user = create(:user) user.enterprise_roles.create(enterprise: supplier) user.spree_roles = [] @@ -76,7 +76,7 @@ module OpenFoodNetwork describe "fetching child variants" do it "returns some variants" do product1 = create(:simple_product, supplier: supplier) - variant_1 = create(:variant, product: product1) + variant_1 = product1.variants.first variant_2 = create(:variant, product: product1) subject.child_variants.sort.should == [variant_1, variant_2].sort @@ -85,41 +85,34 @@ module OpenFoodNetwork it "should only return variants managed by the user" do product1 = create(:simple_product, supplier: create(:supplier_enterprise)) product2 = create(:simple_product, supplier: supplier) - variant_1 = create(:variant, product: product1) - variant_2 = create(:variant, product: product2) - + variant_1 = product1.variants.first + variant_2 = product2.variants.first + subject.child_variants.should == [variant_2] end end describe "fetching master variants" do - it "should only return variants managed by the user" do - product1 = create(:simple_product, supplier: create(:supplier_enterprise)) - product2 = create(:simple_product, supplier: supplier) - - subject.master_variants.should == [product2.master] - end - it "doesn't return master variants with siblings" do product = create(:simple_product, supplier: supplier) - create(:variant, product: product) - - subject.master_variants.should be_empty + + subject.master_variants.should be_empty end end describe "Filtering variants" do - let(:variants) { Spree::Variant.scoped.joins(:product) } + let(:variants) { Spree::Variant.scoped.joins(:product).where(is_master: false) } it "should return unfiltered variants sans-params" do product1 = create(:simple_product, supplier: supplier) product2 = create(:simple_product, supplier: supplier) - subject.filter(Spree::Variant.scoped).sort.should == [product1.master, product2.master].sort + + subject.filter(Spree::Variant.scoped).sort.should == [product1.master, product1.variants.first, product2.master, product2.variants.first].sort end it "should filter deleted products" do product1 = create(:simple_product, supplier: supplier) product2 = create(:simple_product, supplier: supplier) product2.delete - subject.filter(Spree::Variant.scoped).sort.should == [product1.master].sort + subject.filter(Spree::Variant.scoped).sort.should == [product1.master, product1.variants.first].sort end describe "based on report type" do it "returns only variants on hand" do @@ -127,7 +120,7 @@ module OpenFoodNetwork product2 = create(:simple_product, supplier: supplier, on_hand: 0) subject.stub(:params).and_return(report_type: 'inventory') - subject.filter(variants).should == [product1.master] + subject.filter(variants).should == [product1.variants.first] end end it "filters to a specific supplier" do @@ -136,7 +129,7 @@ module OpenFoodNetwork product2 = create(:simple_product, supplier: supplier2) subject.stub(:params).and_return(supplier_id: supplier.id) - subject.filter(variants).should == [product1.master] + subject.filter(variants).should == [product1.variants.first] end it "filters to a specific distributor" do distributor = create(:distributor_enterprise) @@ -144,23 +137,23 @@ module OpenFoodNetwork product2 = create(:simple_product, supplier: supplier, distributors: [distributor]) subject.stub(:params).and_return(distributor_id: distributor.id) - subject.filter(variants).should == [product2.master] + subject.filter(variants).should == [product2.variants.first] end it "filters to a specific order cycle" do distributor = create(:distributor_enterprise) product1 = create(:simple_product, supplier: supplier, distributors: [distributor]) product2 = create(:simple_product, supplier: supplier, distributors: [distributor]) - order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product1.master]) + order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product1.variants.first]) subject.stub(:params).and_return(order_cycle_id: order_cycle.id) - subject.filter(variants).should == [product1.master] + subject.filter(variants).should == [product1.variants.first] end it "should do all the filters at once" do distributor = create(:distributor_enterprise) product1 = create(:simple_product, supplier: supplier, distributors: [distributor]) product2 = create(:simple_product, supplier: supplier, distributors: [distributor]) - order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product1.master]) + order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product1.variants.first]) subject.stub(:params).and_return( order_cycle_id: order_cycle.id, diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 97a4b988d8..47a64c06a9 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -693,45 +693,38 @@ describe Enterprise do end describe "finding variants distributed by the enterprise" do - it "finds the master variant" do + it "finds master and other variants" do d = create(:distributor_enterprise) p = create(:product, distributors: [d]) - d.distributed_variants.should == [p.master] - end - - it "finds other variants" do - d = create(:distributor_enterprise) - p = create(:product, distributors: [d]) - v = create(:variant, product: p) + v = p.variants.first d.distributed_variants.sort.should == [p.master, v].sort end - it "finds variants distributed by order cycle" do + pending "finds variants distributed by order cycle" do + # there isn't actually a method for this on Enterprise? d = create(:distributor_enterprise) p = create(:product) - oc = create(:simple_order_cycle, distributors: [d], variants: [p.master]) - d.distributed_variants.should == [p.master] + v = p.variants.first + oc = create(:simple_order_cycle, distributors: [d], variants: [v]) + + # This method doesn't do what this test says it does... + d.distributed_variants.sort.should == [v] end end describe "finding variants distributed by the enterprise in a product distribution only" do - it "finds the master variant" do + it "finds master and other variants" do d = create(:distributor_enterprise) p = create(:product, distributors: [d]) - d.product_distribution_variants.should == [p.master] - end - - it "finds other variants" do - d = create(:distributor_enterprise) - p = create(:product, distributors: [d]) - v = create(:variant, product: p) + v = p.variants.first d.product_distribution_variants.sort.should == [p.master, v].sort end it "does not find variants distributed by order cycle" do d = create(:distributor_enterprise) p = create(:product) - oc = create(:simple_order_cycle, distributors: [d], variants: [p.master]) + v = p.variants.first + oc = create(:simple_order_cycle, distributors: [d], variants: [v]) d.product_distribution_variants.should == [] end end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 817d2e8e93..5f95a09a8f 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -239,10 +239,12 @@ describe OrderCycle do it "returns valid products but not invalid products" do p_valid = create(:product) p_invalid = create(:product) - v = create(:variant, product: p_invalid) + v_valid = p_valid.variants.first + v_invalid = p_invalid.variants.first d = create(:distributor_enterprise) - oc = create(:simple_order_cycle, distributors: [d], variants: [p_valid.master, p_invalid.master]) + oc = create(:simple_order_cycle, distributors: [d], variants: [v_valid, p_invalid.master]) + oc.valid_products_distributed_by(d).should == [p_valid] end @@ -313,7 +315,7 @@ describe OrderCycle do @oc.pickup_time_for(@d2).should == '2-8pm Friday' end end - + describe "finding pickup instructions for a distributor" do it "returns the pickup instructions" do @oc.pickup_instructions_for(@d1).should == "Come get it!" @@ -375,7 +377,7 @@ describe OrderCycle do occ.coordinator_fee_ids.should_not be_empty occ.coordinator_fee_ids.should == oc.coordinator_fee_ids - + # to_h gives us a unique hash for each exchange # check that the clone has no additional exchanges occ.exchanges.map(&:to_h).all? do |ex| @@ -402,7 +404,7 @@ describe OrderCycle do describe "finding order cycles opening in the future" do it "should give the soonest opening order cycle for a distributor" do distributor = create(:distributor_enterprise) - oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 10.days.from_now, orders_close_at: 11.days.from_now) + oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 10.days.from_now, orders_close_at: 11.days.from_now) OrderCycle.first_opening_for(distributor).should == oc end @@ -411,12 +413,12 @@ describe OrderCycle do OrderCycle.first_opening_for(distributor).should == nil end end - + describe "finding open order cycles" do it "should give the soonest closing order cycle for a distributor" do distributor = create(:distributor_enterprise) - oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 1.days.ago, orders_close_at: 11.days.from_now) - oc2 = create(:simple_order_cycle, name: 'oc 2', distributors: [distributor], orders_open_at: 2.days.ago, orders_close_at: 12.days.from_now) + oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 1.days.ago, orders_close_at: 11.days.from_now) + oc2 = create(:simple_order_cycle, name: 'oc 2', distributors: [distributor], orders_open_at: 2.days.ago, orders_close_at: 12.days.from_now) OrderCycle.first_closing_for(distributor).should == oc end end diff --git a/spec/requests/shop_spec.rb b/spec/requests/shop_spec.rb index c13cfc675f..cc8838220c 100644 --- a/spec/requests/shop_spec.rb +++ b/spec/requests/shop_spec.rb @@ -7,72 +7,57 @@ describe "Shop API" do let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) } let(:supplier) { create(:supplier_enterprise) } let(:oc1) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), orders_close_at: 2.days.from_now) } - let(:p1) { create(:simple_product, on_demand: false) } - let(:p2) { create(:simple_product, on_demand: true) } - let(:p3) { create(:simple_product, on_demand: false) } let(:p4) { create(:simple_product, on_demand: false) } let(:p5) { create(:simple_product, on_demand: false) } let(:p6) { create(:simple_product, on_demand: false) } let(:p7) { create(:simple_product, on_demand: false) } - let(:v1) { create(:variant, product: p4, unit_value: 2) } - let(:v2) { create(:variant, product: p4, unit_value: 3, on_demand: false) } - let(:v3) { create(:variant, product: p4, unit_value: 4, on_demand: true) } - let(:v4) { create(:variant, product: p5) } - let(:v5) { create(:variant, product: p5) } - let(:v6) { create(:variant, product: p7) } + let(:v41) { p4.variants.first } + let(:v42) { create(:variant, product: p4, unit_value: 3, on_demand: false) } + let(:v43) { create(:variant, product: p4, unit_value: 4, on_demand: true) } + let(:v51) { p5.variants.first } + let(:v52) { create(:variant, product: p5) } + let(:v61) { p6.variants.first } + let(:v71) { p7.variants.first } let(:order) { create(:order, distributor: distributor, order_cycle: oc1) } before do set_order order - p1.master.update_attribute(:count_on_hand, 1) - p2.master.update_attribute(:count_on_hand, 0) - p3.master.update_attribute(:count_on_hand, 0) - p6.master.update_attribute(:count_on_hand, 1) + v61.update_attribute(:count_on_hand, 1) p6.delete - p7.master.update_attribute(:count_on_hand, 1) - v1.update_attribute(:count_on_hand, 1) - v2.update_attribute(:count_on_hand, 0) - v3.update_attribute(:count_on_hand, 0) - v4.update_attribute(:count_on_hand, 1) - v5.update_attribute(:count_on_hand, 0) - v6.update_attribute(:count_on_hand, 1) - v6.update_attribute(:deleted_at, Time.now) - exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) - exchange.update_attribute :pickup_time, "frogs" - exchange.variants << p1.master - exchange.variants << p2.master - exchange.variants << p3.master - exchange.variants << p6.master - exchange.variants << v1 - exchange.variants << v2 - exchange.variants << v3 - # v4 is in stock but not in distribution - # v5 is out of stock and in the distribution + v71.update_attribute(:count_on_hand, 1) + v41.update_attribute(:count_on_hand, 1) + v42.update_attribute(:count_on_hand, 0) + v43.update_attribute(:count_on_hand, 0) + v51.update_attribute(:count_on_hand, 1) + v52.update_attribute(:count_on_hand, 0) + v71.update_attribute(:count_on_hand, 1) + v71.update_attribute(:deleted_at, Time.now) + exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) + exchange.update_attribute :pickup_time, "frogs" + exchange.variants << v61 + exchange.variants << v41 + exchange.variants << v42 + exchange.variants << v43 + # v51 is in stock but not in distribution + # v52 is out of stock and in the distribution # Neither should display, nor should their product, p5 - exchange.variants << v5 - exchange.variants << v6 + exchange.variants << v52 + exchange.variants << v71 get products_shop_path end it "filters products based on availability" do - # It shows on hand products - response.body.should include p1.name - response.body.should include p4.name - # It shows on demand products - response.body.should include p2.name - # It does not show products that are neither on hand or on demand - response.body.should_not include p3.name # It shows on demand variants - response.body.should include v3.options_text + response.body.should include v43.options_text # It does not show variants that are neither on hand or on demand - response.body.should_not include v2.options_text + response.body.should_not include v42.options_text # It does not show products that have no available variants in this distribution response.body.should_not include p5.name # It does not show deleted products response.body.should_not include p6.name # It does not show deleted variants - response.body.should_not include v6.name + response.body.should_not include v71.name response.body.should_not include p7.name end end From 63353ebace437410161d1456c3668537372bc525 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 17 Apr 2015 16:49:14 +1000 Subject: [PATCH 025/151] Don't try and delete the only variant, that will never work! --- spec/features/admin/variants_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb index c947872949..910f299386 100644 --- a/spec/features/admin/variants_spec.rb +++ b/spec/features/admin/variants_spec.rb @@ -60,12 +60,14 @@ feature %q{ it "soft-deletes variants", js: true do p = create(:simple_product) - v = p.variants.first + v = create(:variant, product: p) login_to_admin_section visit spree.admin_product_variants_path p - page.find('a.delete-resource').click + within "tr#spree_variant_#{v.id}" do + page.find('a.delete-resource').click + end page.should_not have_content v.options_text v.reload From 72d553ef0c12bf7cf02c4123b613e11b3dc52115 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 17 Apr 2015 16:51:02 +1000 Subject: [PATCH 026/151] Test actual deletion of variants --- spec/models/spree/product_spec.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 42c85e98e3..d6e814fed2 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -29,11 +29,7 @@ module Spree product.on_hand = "10,000" expect(product.save).to be_false - # It is expected that master variant :count_on_hand should cause the failure to save, and it does. However, this broke with the addition of - # the :ensure_standard_variant callback. Evidently, normal variants are checked and their errors added to the product model before the - # save_master callback on product is run. Therefore, the error that is raised here is one on a normal variant, rather than one on master. - # expect(product.errors[:count_on_hand]).to include "is not a number" - expect(product.errors[:"variants.count_on_hand"]).to include "is not a number" + expect(product.errors[:count_on_hand]).to include "is not a number" end it "defaults available_on to now" do @@ -64,9 +60,9 @@ module Spree it "does not allow the last variant to be deleted" do product = create(:simple_product) expect(product.variants(:reload).length).to eq 1 - product.variants = [] - expect(product.save).to be_false - expect(product.errors[:variants]).to include "Product must have at least one variant" + v = product.variants.last + v.delete + expect(v.errors[:product]).to include "must have at least one variant" end context "when the product has variants" do From 03fd148f418b285be35c145e9f3a25937535ce38 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 17 Apr 2015 16:58:49 +1000 Subject: [PATCH 027/151] showing profile modals on groups/hubs --- .../controllers/group_enterprises_controller.js.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee index db1a9a1d8b..ea652b8105 100644 --- a/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee @@ -1,9 +1,10 @@ -Darkswarm.controller "GroupEnterprisesCtrl", ($scope, Search, FilterSelectorsService) -> +Darkswarm.controller "GroupEnterprisesCtrl", ($scope, Search, FilterSelectorsService, EnterpriseModal) -> $scope.totalActive = FilterSelectorsService.totalActive $scope.clearAll = FilterSelectorsService.clearAll $scope.filterText = FilterSelectorsService.filterText $scope.FilterSelectorsService = FilterSelectorsService $scope.query = Search.search() + $scope.openModal = EnterpriseModal.open $scope.activeTaxons = [] $scope.show_profiles = false $scope.filtersActive = false From 893b743973fceb8bf3f610597b30ea5df1d302ee Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 17 Apr 2015 17:00:18 +1000 Subject: [PATCH 028/151] tidy (rm comment) --- .../darkswarm/controllers/groups_controller.js.coffee | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee index 4bb856ace4..aafb7597d6 100644 --- a/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/groups_controller.js.coffee @@ -1,8 +1,3 @@ Darkswarm.controller "GroupsCtrl", ($scope, Groups, $anchorScroll, $rootScope) -> $scope.Groups = Groups $scope.order = 'position' - - #$rootScope.$on "$locationChangeSuccess", (newRoute, oldRoute) -> - #$anchorScroll() - # - # From ff2e6d9ca46806af706181c9087e77c603823826 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 17 Apr 2015 17:25:13 +1000 Subject: [PATCH 029/151] Test deletion rather than destruction on variant model spec --- app/models/spree/variant_decorator.rb | 2 ++ spec/models/spree/variant_spec.rb | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 20c4f20cf7..88c9b14e26 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -104,10 +104,12 @@ Spree::Variant.class_eval do def delete if product.variants == [self] # Only variant left on product errors.add :product, "must have at least one variant" + false else transaction do self.update_column(:deleted_at, Time.now) ExchangeVariant.where(variant_id: self).destroy_all + self end end end diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 2c37065916..7910087dfd 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -401,15 +401,17 @@ module Spree end context "as the last variant of a product" do - let!(:product) { create(:simple_product) } - let!(:first_variant) { product.variants(:reload).first } - let!(:extra_variant) { create(:variant, product: product) } + let!(:extra_variant) { create(:variant) } + let!(:product) { extra_variant.product } + let!(:first_variant) { product.variants.first } + + before { product.reload } it "cannot be deleted" do - expect(product.variants(:reload).length).to eq 2 - expect(extra_variant.destroy).to eq extra_variant + expect(product.variants.length).to eq 2 + expect(extra_variant.delete).to eq extra_variant expect(product.variants(:reload).length).to eq 1 - expect(first_variant.destroy).to be_false + expect(first_variant.delete).to be_false expect(product.variants(:reload).length).to eq 1 expect(first_variant.errors[:product]).to include "must have at least one variant" end From 05c350b5ff482d8d3baf533179b8ee7dfc965694 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 24 Apr 2015 11:27:47 +1000 Subject: [PATCH 030/151] Refactoring unitsCtrl --- .../admin/products/units_controller.js.coffee | 12 ++-- .../products/units_controller_spec.js.coffee | 55 +++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 spec/javascripts/unit/admin/products/units_controller_spec.js.coffee diff --git a/app/assets/javascripts/admin/products/units_controller.js.coffee b/app/assets/javascripts/admin/products/units_controller.js.coffee index db005e490a..c9c5807af3 100644 --- a/app/assets/javascripts/admin/products/units_controller.js.coffee +++ b/app/assets/javascripts/admin/products/units_controller.js.coffee @@ -5,6 +5,13 @@ angular.module("admin.products") $scope.placeholder_text = "" $scope.$watchCollection '[product.variant_unit_with_scale, product.master.unit_value_with_description]', -> + $scope.processVariantUnitWithScale() + $scope.processUnitValueWithDescription() + $scope.placeholder_text = new OptionValueNamer($scope.product.master).name() + + $scope.variant_unit_options = VariantUnitManager.variantUnitOptions() + + $scope.processVariantUnitWithScale = -> if $scope.product.variant_unit_with_scale match = $scope.product.variant_unit_with_scale.match(/^([^_]+)_([\d\.]+)$/) if match @@ -16,6 +23,7 @@ angular.module("admin.products") else $scope.product.variant_unit = $scope.product.variant_unit_scale = null + $scope.processUnitValueWithDescription = -> if $scope.product.master.hasOwnProperty("unit_value_with_description") match = $scope.product.master.unit_value_with_description.match(/^([\d\.]+(?= |$)|)( |)(.*)$/) if match @@ -24,10 +32,6 @@ angular.module("admin.products") $scope.product.master.unit_value *= $scope.product.variant_unit_scale if $scope.product.master.unit_value && $scope.product.variant_unit_scale $scope.product.master.unit_description = match[3] - $scope.placeholder_text = new OptionValueNamer($scope.product.master).name() - - $scope.variant_unit_options = VariantUnitManager.variantUnitOptions() - $scope.hasVariants = (product) -> Object.keys(product.variants).length > 0 diff --git a/spec/javascripts/unit/admin/products/units_controller_spec.js.coffee b/spec/javascripts/unit/admin/products/units_controller_spec.js.coffee new file mode 100644 index 0000000000..c5f104f07a --- /dev/null +++ b/spec/javascripts/unit/admin/products/units_controller_spec.js.coffee @@ -0,0 +1,55 @@ +describe "unitsCtrl", -> + ctrl = null + scope = null + product = null + + beforeEach -> + module('admin.products') + inject ($rootScope, $controller, VariantUnitManager) -> + scope = $rootScope + ctrl = $controller 'unitsCtrl', {$scope: scope, VariantUnitManager: VariantUnitManager} + + describe "interpretting variant_unit_with_scale", -> + it "splits string with one underscore and stores the two parts", -> + scope.product.variant_unit_with_scale = "weight_1000" + scope.processVariantUnitWithScale() + expect(scope.product.variant_unit).toEqual "weight" + expect(scope.product.variant_unit_scale).toEqual 1000 + + it "interprets strings with no underscore as variant_unit", -> + scope.product.variant_unit_with_scale = "items" + scope.processVariantUnitWithScale() + expect(scope.product.variant_unit).toEqual "items" + expect(scope.product.variant_unit_scale).toEqual null + + it "sets variant_unit and variant_unit_scale to null", -> + scope.product.variant_unit_with_scale = null + scope.processVariantUnitWithScale() + expect(scope.product.variant_unit).toEqual null + expect(scope.product.variant_unit_scale).toEqual null + + describe "interpretting unit_value_with_description", -> + beforeEach -> + scope.product.master = {} + + describe "when a variant_unit_scale is present", -> + beforeEach -> + scope.product.variant_unit_scale = 1 + + it "splits by whitespace in to unit_value and unit_description", -> + scope.product.master.unit_value_with_description = "12 boxes" + scope.processUnitValueWithDescription() + expect(scope.product.master.unit_value).toEqual 12 + expect(scope.product.master.unit_description).toEqual "boxes" + + it "uses whole string as unit_value when only numerical characters are present", -> + scope.product.master.unit_value_with_description = "12345" + scope.processUnitValueWithDescription() + expect(scope.product.master.unit_value).toEqual 12345 + expect(scope.product.master.unit_description).toEqual '' + + it "uses whole string as description when string does not start with a number", -> + scope.product.master.unit_value_with_description = "boxes 12" + scope.processUnitValueWithDescription() + expect(scope.product.master.unit_value).toEqual null + expect(scope.product.master.unit_description).toEqual "boxes 12" From ed7b763ecf3152dac7ada2eb5d0d51a25425dc06 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 24 Apr 2015 12:15:35 +1000 Subject: [PATCH 031/151] UnitsCtrl can interpret unit_value_with_description without a separating space --- .../admin/products/units_controller.js.coffee | 2 +- .../admin/products/units_controller_spec.js.coffee | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/products/units_controller.js.coffee b/app/assets/javascripts/admin/products/units_controller.js.coffee index c9c5807af3..a84f048580 100644 --- a/app/assets/javascripts/admin/products/units_controller.js.coffee +++ b/app/assets/javascripts/admin/products/units_controller.js.coffee @@ -25,7 +25,7 @@ angular.module("admin.products") $scope.processUnitValueWithDescription = -> if $scope.product.master.hasOwnProperty("unit_value_with_description") - match = $scope.product.master.unit_value_with_description.match(/^([\d\.]+(?= |$)|)( |)(.*)$/) + match = $scope.product.master.unit_value_with_description.match(/^([\d\.]+(?= *|$)|)( *)(.*)$/) if match $scope.product.master.unit_value = parseFloat(match[1]) $scope.product.master.unit_value = null if isNaN($scope.product.master.unit_value) diff --git a/spec/javascripts/unit/admin/products/units_controller_spec.js.coffee b/spec/javascripts/unit/admin/products/units_controller_spec.js.coffee index c5f104f07a..b846126711 100644 --- a/spec/javascripts/unit/admin/products/units_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/products/units_controller_spec.js.coffee @@ -53,3 +53,15 @@ describe "unitsCtrl", -> scope.processUnitValueWithDescription() expect(scope.product.master.unit_value).toEqual null expect(scope.product.master.unit_description).toEqual "boxes 12" + + it "does not require whitespace to split unit value and description", -> + scope.product.master.unit_value_with_description = "12boxes" + scope.processUnitValueWithDescription() + expect(scope.product.master.unit_value).toEqual 12 + expect(scope.product.master.unit_description).toEqual "boxes" + + it "once a whitespace occurs, all subsequent numerical characters are counted as description", -> + scope.product.master.unit_value_with_description = "123 54 boxes" + scope.processUnitValueWithDescription() + expect(scope.product.master.unit_value).toEqual 123 + expect(scope.product.master.unit_description).toEqual "54 boxes" From 162a5651401d2a8288123e4a807e3d576218017a Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Fri, 24 Apr 2015 16:14:24 +0100 Subject: [PATCH 032/151] Removing notes to myself from this file --- .../spree/api/line_items_controller_decorator.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/controllers/spree/api/line_items_controller_decorator.rb b/app/controllers/spree/api/line_items_controller_decorator.rb index f9a025873d..35fca864f4 100644 --- a/app/controllers/spree/api/line_items_controller_decorator.rb +++ b/app/controllers/spree/api/line_items_controller_decorator.rb @@ -6,12 +6,3 @@ Spree::Api::LineItemsController.class_eval do order.update_distribution_charge! end end - - -#when we update a line item the .update_distribution_charge! is called -# order.should_receive .update_distribution_charge! -# check fails when absent - -# in order model check that .update_distribution_charge! is properly tested. -# think through use cases - existing completed order -# currently likely just used to complete orders so add test case that works on a completed order From 60452835497ee52b36c1264e4b214837f8390ffb Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Fri, 24 Apr 2015 16:17:00 +0100 Subject: [PATCH 033/151] Populate the line item unit value, when line_item created and update old data in migration --- app/models/spree/order_decorator.rb | 1 + .../20150424151117_populate_line_item_unit_value.rb | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 db/migrate/20150424151117_populate_line_item_unit_value.rb diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 9d213bd633..a7158096dd 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -127,6 +127,7 @@ Spree::Order.class_eval do else current_item = Spree::LineItem.new(:quantity => quantity, max_quantity: max_quantity) current_item.variant = variant + current_item.unit_value = variant.unit_value if currency current_item.currency = currency unless currency.nil? current_item.price = variant.price_in(currency).amount diff --git a/db/migrate/20150424151117_populate_line_item_unit_value.rb b/db/migrate/20150424151117_populate_line_item_unit_value.rb new file mode 100644 index 0000000000..2122b84472 --- /dev/null +++ b/db/migrate/20150424151117_populate_line_item_unit_value.rb @@ -0,0 +1,9 @@ +class PopulateLineItemUnitValue < ActiveRecord::Migration + def up + execute "UPDATE spree_line_items SET unit_value = spree_variants.unit_value FROM spree_variants WHERE spree_line_items.variant_id = spree_variants.id" + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end From d344c3dec8da637abe28753a61509077e6884ed7 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Fri, 24 Apr 2015 16:22:17 +0100 Subject: [PATCH 034/151] Updating the spec based on @Robs suggestions, hoping for his insights. Still doesn't work. --- spec/controllers/spree/api/line_items_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/spree/api/line_items_controller_spec.rb b/spec/controllers/spree/api/line_items_controller_spec.rb index e3c9a262af..7d0b215ae9 100644 --- a/spec/controllers/spree/api/line_items_controller_spec.rb +++ b/spec/controllers/spree/api/line_items_controller_spec.rb @@ -20,9 +20,9 @@ module Spree make_simple_data! context "as a line item is updated" do - it "apply enterprise fees on the item" do + it "update distribution charge on the order" do line_item_params = { order_id: order.number, id: line_item.id, line_item: { id: line_item.id, unit_value: 520 }, format: :json} - controller.should_receive(:apply_enterprise_fees).and_return(true) + order.should_receive(:update_distribution_charge!).and_call_original spree_post :update, line_item_params end end From 6bbd3f7c13182f3eb1918539113c3b5c1f5699ce Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sun, 26 Apr 2015 11:02:06 +0100 Subject: [PATCH 035/151] Added auth for order_cycle_management_report. This report was breaking supplier enterprises reports due to incorrect authorization. --- app/models/spree/ability_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 9bff908346..dbb0d4fe10 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -112,7 +112,7 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit], Spree::Classification # Reports page - can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory], :report + can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], :report end def add_order_management_abilities(user) From 9e61a7d083da2134a44cbf56e3a01ee00f677fd7 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sun, 26 Apr 2015 11:03:32 +0100 Subject: [PATCH 036/151] Adding report type drop down to order_cycle_management_report --- .../spree/admin/reports/order_cycle_management.html.haml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/spree/admin/reports/order_cycle_management.html.haml b/app/views/spree/admin/reports/order_cycle_management.html.haml index fbe365c742..418cba8b75 100644 --- a/app/views/spree/admin/reports/order_cycle_management.html.haml +++ b/app/views/spree/admin/reports/order_cycle_management.html.haml @@ -20,6 +20,10 @@ include_blank: true) %br %br + = label_tag nil, "Report Type: " + = select_tag(:report_type, options_for_select(@report_types, @report_type)) + %br + %br = check_box_tag :csv = label_tag :csv, "Download as csv" %br @@ -41,4 +45,3 @@ - if @report.table.empty? %tr %td{:colspan => "2"}= t(:none) - From a253b8852517d327c9b6fa00c64edb041b619c47 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 29 Apr 2015 10:42:38 +1000 Subject: [PATCH 037/151] Fixing line item controller spec --- spec/controllers/spree/api/line_items_controller_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/controllers/spree/api/line_items_controller_spec.rb b/spec/controllers/spree/api/line_items_controller_spec.rb index 7d0b215ae9..37ec50eb7e 100644 --- a/spec/controllers/spree/api/line_items_controller_spec.rb +++ b/spec/controllers/spree/api/line_items_controller_spec.rb @@ -22,7 +22,8 @@ module Spree context "as a line item is updated" do it "update distribution charge on the order" do line_item_params = { order_id: order.number, id: line_item.id, line_item: { id: line_item.id, unit_value: 520 }, format: :json} - order.should_receive(:update_distribution_charge!).and_call_original + allow(controller).to receive(:order) { order } + expect(order).to receive(:update_distribution_charge!) spree_post :update, line_item_params end end From 295da25dd23aef786b845eee8ecf26025bee2e2c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 30 Apr 2015 17:17:28 +1000 Subject: [PATCH 038/151] insert clone after cloned product --- .../javascripts/admin/services/bulk_products.js.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/services/bulk_products.js.coffee b/app/assets/javascripts/admin/services/bulk_products.js.coffee index d898356846..6085d48fcc 100644 --- a/app/assets/javascripts/admin/services/bulk_products.js.coffee +++ b/app/assets/javascripts/admin/services/bulk_products.js.coffee @@ -19,7 +19,7 @@ angular.module("ofn.admin").factory "BulkProducts", (PagedFetcher, dataFetcher) # when a respond_overrride for the clone action is used. id = data.product.id dataFetcher("/api/products/" + id + "?template=bulk_show").then (newProduct) => - @addProducts [newProduct] + @insertProductAfter(product, newProduct) updateVariantLists: (serverProducts, productsWithUnsavedVariants) -> for product in productsWithUnsavedVariants @@ -39,6 +39,10 @@ angular.module("ofn.admin").factory "BulkProducts", (PagedFetcher, dataFetcher) @unpackProduct product @products.push product + insertProductAfter: (product, newProduct) -> + index = @products.indexOf(product) + @products.splice(index + 1, 0, newProduct) + unpackProduct: (product) -> #$scope.matchProducer product @loadVariantUnit product From 66f847f673cd47c82238b094f7620a7c4ab1e983 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 30 Apr 2015 17:22:54 +1000 Subject: [PATCH 039/151] showing save button at the bottom as well --- app/views/spree/admin/products/bulk_edit.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml index f230d1c331..383e080177 100644 --- a/app/views/spree/admin/products/bulk_edit.html.haml +++ b/app/views/spree/admin/products/bulk_edit.html.haml @@ -8,3 +8,4 @@ = render 'spree/admin/products/bulk_edit/actions' = render 'spree/admin/products/bulk_edit/indicators' = render 'spree/admin/products/bulk_edit/products' + = render 'spree/admin/products/bulk_edit/actions' From baabb5c07fa11d94a9ddbff464bdcf929152e823 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 1 May 2015 11:30:26 +1000 Subject: [PATCH 040/151] fixing BPE feature spec --- .../spree/admin/products/bulk_edit.html.haml | 2 +- .../bulk_edit/_save_button_row.html.haml | 3 ++ .../admin/bulk_product_update_spec.rb | 28 +++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) create mode 100644 app/views/spree/admin/products/bulk_edit/_save_button_row.html.haml diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml index 383e080177..22b0a195da 100644 --- a/app/views/spree/admin/products/bulk_edit.html.haml +++ b/app/views/spree/admin/products/bulk_edit.html.haml @@ -8,4 +8,4 @@ = render 'spree/admin/products/bulk_edit/actions' = render 'spree/admin/products/bulk_edit/indicators' = render 'spree/admin/products/bulk_edit/products' - = render 'spree/admin/products/bulk_edit/actions' + = render 'spree/admin/products/bulk_edit/save_button_row' diff --git a/app/views/spree/admin/products/bulk_edit/_save_button_row.html.haml b/app/views/spree/admin/products/bulk_edit/_save_button_row.html.haml new file mode 100644 index 0000000000..a5f18986c0 --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_save_button_row.html.haml @@ -0,0 +1,3 @@ +%div.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 0', style: "margin-bottom: 10px" } + %div.four.columns.alpha + %input.four.columns.alpha{ :type => 'button', :value => 'Save Changes', 'ng-click' => 'submitProducts()'} diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index ac101da5d9..089f3188f7 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -266,7 +266,7 @@ feature %q{ fill_in "variant_display_as", with: "Case" fill_in "variant_price", with: "4.0" fill_in "variant_on_hand", with: "10" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." updated_variant = Spree::Variant.where(deleted_at: nil).last @@ -325,7 +325,7 @@ feature %q{ fill_in "product_sku", with: "NEW SKU" end - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload @@ -354,7 +354,7 @@ feature %q{ select "Items", from: "variant_unit_with_scale" fill_in "variant_unit_name", with: "loaf" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload @@ -377,7 +377,7 @@ feature %q{ first("a.view-variants").trigger('click') fill_in "variant_unit_value_with_description", with: '123 abc' - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload @@ -402,7 +402,7 @@ feature %q{ select "Weight (kg)", from: "variant_unit_with_scale" fill_in "master_unit_value_with_description", with: '123 abc' - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload @@ -450,7 +450,7 @@ feature %q{ expect(page).to have_selector "span[name='on_hand']", text: "10" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." v.reload @@ -474,7 +474,7 @@ feature %q{ fill_in "variant_price", with: "10.0" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." v.reload @@ -491,21 +491,21 @@ feature %q{ fill_in "product_name", with: "new name 1" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload expect(p.name).to eq "new name 1" fill_in "product_name", with: "new name 2" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload expect(p.name).to eq "new name 2" fill_in "product_name", with: "original name" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload expect(p.name).to eq "original name" @@ -521,7 +521,7 @@ feature %q{ fill_in "product_name", :with => "new product name" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload expect(p.name).to eq "new product name" @@ -534,7 +534,7 @@ feature %q{ visit '/admin/products/bulk_edit' - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "No changes to save." end end @@ -553,7 +553,7 @@ feature %q{ expect(page).to have_no_field "product_name", with: p2.name fill_in "product_name", :with => "new product1" - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p1.reload expect(p1.name).to eq "new product1" @@ -829,7 +829,7 @@ feature %q{ fill_in "display_as", with: "Big Bag" end - click_button 'Save Changes' + first(:button, 'Save Changes').click expect(page.find("#status-message")).to have_content "Changes saved." p.reload From 993183f2f57b79c8d3d981e60a4d47fde759df60 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 1 May 2015 15:04:12 +1000 Subject: [PATCH 041/151] updating js spec: cloning product calls insertProductAfter now --- .../unit/admin/services/bulk_products_spec.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee b/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee index 70c22c80e0..0b3c0841a6 100644 --- a/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee @@ -61,7 +61,7 @@ describe "BulkProducts service", -> clonedProduct = id: 17 - spyOn(BulkProducts, "addProducts") + spyOn(BulkProducts, "insertProductAfter") BulkProducts.products = [originalProduct] $httpBackend.expectGET("/admin/products/oranges/clone.json").respond 200, product: clonedProduct From 89b153dc2c8fcd899fe1606b0c8f09cb06a4c4ce Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 29 Apr 2015 13:43:11 +1000 Subject: [PATCH 042/151] Update AJAX request to use sells instead of deprecated is_distributor attribute --- .../javascripts/admin/bulk_order_management.js.coffee | 2 +- spec/javascripts/unit/bulk_order_management_spec.js.coffee | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 4c1a319c1a..03d0890408 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -44,7 +44,7 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ dataFetcher("/api/enterprises/accessible?template=bulk_index&q[is_primary_producer_eq]=true").then (data) -> $scope.suppliers = data $scope.suppliers.unshift blankOption() - dataFetcher("/api/enterprises/accessible?template=bulk_index&q[is_distributor_eq]=true").then (data) -> + dataFetcher("/api/enterprises/accessible?template=bulk_index&q[sells_in][]=own&q[sells_in][]=any").then (data) -> $scope.distributors = data $scope.distributors.unshift blankOption() ocFetcher = dataFetcher("/api/order_cycles/accessible").then (data) -> diff --git a/spec/javascripts/unit/bulk_order_management_spec.js.coffee b/spec/javascripts/unit/bulk_order_management_spec.js.coffee index 35b9e13d61..61164870ed 100644 --- a/spec/javascripts/unit/bulk_order_management_spec.js.coffee +++ b/spec/javascripts/unit/bulk_order_management_spec.js.coffee @@ -22,7 +22,7 @@ describe "AdminOrderMgmtCtrl", -> returnedOrderCycles = [ "oc1", "oc2", "oc3" ] httpBackend.expectGET("/api/users/authorise_api?token=API_KEY").respond success: "Use of API Authorised" httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[is_primary_producer_eq]=true").respond returnedSuppliers - httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[is_distributor_eq]=true").respond returnedDistributors + httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[sells_in][]=own&q[sells_in][]=any").respond returnedDistributors httpBackend.expectGET("/api/order_cycles/accessible").respond returnedOrderCycles spyOn(scope, "initialiseVariables").andCallThrough() spyOn(scope, "fetchOrders").andReturn "nothing" @@ -33,8 +33,8 @@ describe "AdminOrderMgmtCtrl", -> httpBackend.flush() expect(scope.suppliers).toEqual [{ id : '0', name : 'All' }, 'list of suppliers'] - expect(scope.distributors).toEqual [ { id : '0', name : 'All' }, 'list of distributors' ] - expect(scope.orderCycles).toEqual [ { id : '0', name : 'All' }, 'oc1', 'oc2', 'oc3' ] + expect(scope.distributors).toEqual [ { id : '0', name : 'All' }, 'list of distributors' ] + expect(scope.orderCycles).toEqual [ { id : '0', name : 'All' }, 'oc1', 'oc2', 'oc3' ] expect(scope.initialiseVariables.calls.length).toBe 1 expect(scope.fetchOrders.calls.length).toBe 1 From d8c23d37ac02c3cf6f4ecf0dbbeef3fffcc54e24 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 29 Apr 2015 13:44:00 +1000 Subject: [PATCH 043/151] Update accessible_by scope on enterprise, to read from permissions --- app/models/enterprise.rb | 6 ++---- spec/models/enterprise_spec.rb | 11 +++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 1aa76b88a8..3cf00730a8 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -162,14 +162,12 @@ class Enterprise < ActiveRecord::Base end } - # Return enterprises that participate in order cycles that user coordinates, sends to or receives from + # Return enterprises that the user manages and those that have granted P-OC to managed enterprises scope :accessible_by, lambda { |user| if user.has_spree_role?('admin') scoped else - with_order_cycles_outer. - where('order_cycles.id IN (?)', OrderCycle.accessible_by(user)). - select('DISTINCT enterprises.*') + where(id: OpenFoodNetwork::Permissions.new(user).order_cycle_enterprises) end } diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 69ba68569f..df5dbbfbc7 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -560,20 +560,19 @@ describe Enterprise do end describe "accessible_by" do - it "shows only enterprises that are invloved in order cycles which are common to those managed by the given user" do + it "shows only managed enterprises and enterprises granting them P-OC" do user = create(:user) user.spree_roles = [] e1 = create(:enterprise) e2 = create(:enterprise) e3 = create(:enterprise) - e4 = create(:enterprise) e1.enterprise_roles.build(user: user).save - oc = create(:simple_order_cycle, coordinator: e2, suppliers: [e1], distributors: [e3]) + create(:enterprise_relationship, parent: e2, child: e1, permissions_list: [:add_to_order_cycle]) enterprises = Enterprise.accessible_by user - enterprises.length.should == 3 - enterprises.should include e1, e2, e3 - enterprises.should_not include e4 + enterprises.length.should == 2 + enterprises.should include e1, e2 + enterprises.should_not include e3 end it "shows all enterprises for admin user" do From 9ab16d8cec54129265bc46237cba8781cc57cc9d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 30 Apr 2015 10:09:51 +1000 Subject: [PATCH 044/151] Allowing calls to Api::OrderCyclesController#accessible to specify :as => 'distributor' or 'producer' --- .../admin/bulk_order_management.js.coffee | 2 +- .../api/order_cycles_controller.rb | 11 +- app/models/order_cycle.rb | 19 +- .../api/order_cycles_controller_spec.rb | 257 ++++++++++++------ .../unit/bulk_order_management_spec.js.coffee | 2 +- 5 files changed, 209 insertions(+), 82 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 03d0890408..5d36718e9a 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -47,7 +47,7 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ dataFetcher("/api/enterprises/accessible?template=bulk_index&q[sells_in][]=own&q[sells_in][]=any").then (data) -> $scope.distributors = data $scope.distributors.unshift blankOption() - ocFetcher = dataFetcher("/api/order_cycles/accessible").then (data) -> + ocFetcher = dataFetcher("/api/order_cycles/accessible?as=distributor&q[orders_close_at_gt]=#{formatDate(daysFromToday(-90))}").then (data) -> $scope.orderCycles = data $scope.orderCycles.unshift blankOption() $scope.fetchOrders() diff --git a/app/controllers/api/order_cycles_controller.rb b/app/controllers/api/order_cycles_controller.rb index b4b3486778..23b333625e 100644 --- a/app/controllers/api/order_cycles_controller.rb +++ b/app/controllers/api/order_cycles_controller.rb @@ -9,7 +9,16 @@ module Api end def accessible - @order_cycles = OrderCycle.ransack(params[:q]).result.accessible_by(current_api_user) + @order_cycles = if params[:as] == "distributor" + OrderCycle.ransack(params[:q]).result. + involving_managed_distributors_of(current_api_user).order('updated_at DESC') + elsif params[:as] == "producer" + OrderCycle.ransack(params[:q]).result. + involving_managed_producers_of(current_api_user).order('updated_at DESC') + else + OrderCycle.ransack(params[:q]).result.accessible_by(current_api_user) + end + render params[:template] || :bulk_index end end diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index adcd596a8c..d16f1d7b8b 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -26,7 +26,7 @@ class OrderCycle < ActiveRecord::Base closed. where("order_cycles.orders_close_at >= ?", 31.days.ago). order("order_cycles.orders_close_at DESC") } - + scope :soonest_opening, lambda { upcoming.order('order_cycles.orders_open_at ASC') } scope :distributing_product, lambda { |product| @@ -64,6 +64,23 @@ class OrderCycle < ActiveRecord::Base joins('LEFT OUTER JOIN enterprises ON (enterprises.id = exchanges.sender_id OR enterprises.id = exchanges.receiver_id)') } + scope :involving_managed_distributors_of, lambda { |user| + enterprises = Enterprise.managed_by(user) + + # Order cycles where I managed an enterprise at either end of an outgoing exchange + # ie. coordinator or distibutor + joins(:exchanges).merge(Exchange.outgoing). + where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises) + } + + scope :involving_managed_producers_of, lambda { |user| + enterprises = Enterprise.managed_by(user) + + # Order cycles where I managed an enterprise at either end of an incoming exchange + # ie. coordinator or producer + joins(:exchanges).merge(Exchange.incoming). + where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises) + } def self.first_opening_for(distributor) with_distributor(distributor).soonest_opening.first diff --git a/spec/controllers/api/order_cycles_controller_spec.rb b/spec/controllers/api/order_cycles_controller_spec.rb index a9cceac796..3bb9a76602 100644 --- a/spec/controllers/api/order_cycles_controller_spec.rb +++ b/spec/controllers/api/order_cycles_controller_spec.rb @@ -4,109 +4,210 @@ require 'spree/api/testing_support/helpers' module Api describe OrderCyclesController do include Spree::Api::TestingSupport::Helpers + include AuthenticationWorkflow render_views - let!(:oc1) { FactoryGirl.create(:simple_order_cycle) } - let!(:oc2) { FactoryGirl.create(:simple_order_cycle) } - let(:coordinator) { oc1.coordinator } - let(:attributes) { [:id, :name, :suppliers, :distributors] } + describe "managed" do + let!(:oc1) { FactoryGirl.create(:simple_order_cycle) } + let!(:oc2) { FactoryGirl.create(:simple_order_cycle) } + let(:coordinator) { oc1.coordinator } + let(:attributes) { [:id, :name, :suppliers, :distributors] } - before do - stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => current_api_user - end + before do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => current_api_user + end - context "as a normal user" do - sign_in_as_user! + context "as a normal user" do + sign_in_as_user! - it "should deny me access to managed order cycles" do - spree_get :managed, { :format => :json } - assert_unauthorized! + it "should deny me access to managed order cycles" do + spree_get :managed, { :format => :json } + assert_unauthorized! + end + end + + context "as an enterprise user" do + sign_in_as_enterprise_user! [:coordinator] + + it "retrieves a list of variants with appropriate attributes" do + get :managed, { :format => :json } + keys = json_response.first.keys.map{ |key| key.to_sym } + attributes.all?{ |attr| keys.include? attr }.should == true + end + end + + context "as an administrator" do + sign_in_as_admin! + + it "retrieves a list of variants with appropriate attributes" do + get :managed, { :format => :json } + keys = json_response.first.keys.map{ |key| key.to_sym } + attributes.all?{ |attr| keys.include? attr }.should == true + end end end - context "as an enterprise user" do - sign_in_as_enterprise_user! [:coordinator] + describe "accessible" do + context "without :as parameter" do + let(:oc_supplier) { create(:supplier_enterprise) } + let(:oc_distributor) { create(:distributor_enterprise) } + let(:other_supplier) { create(:supplier_enterprise) } + let(:oc_supplier_user) do + user = create(:user) + user.spree_roles = [] + user.enterprise_roles.create(enterprise: oc_supplier) + user.save! + user + end + let(:oc_distributor_user) do + user = create(:user) + user.spree_roles = [] + user.enterprise_roles.create(enterprise: oc_distributor) + user.save! + user + end + let(:other_supplier_user) do + user = create(:user) + user.spree_roles = [] + user.enterprise_roles.create(enterprise: other_supplier) + user.save! + user + end + let!(:order_cycle) { create(:simple_order_cycle, suppliers: [oc_supplier], distributors: [oc_distributor]) } - it "retrieves a list of variants with appropriate attributes" do - get :managed, { :format => :json } - keys = json_response.first.keys.map{ |key| key.to_sym } - attributes.all?{ |attr| keys.include? attr }.should == true - end - end + context "as the user of a supplier to an order cycle" do + before :each do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => oc_supplier_user + spree_get :accessible, { :template => 'bulk_index', :format => :json } + end - context "as an administrator" do - sign_in_as_admin! + it "gives me access" do + json_response.length.should == 1 + json_response[0]['id'].should == order_cycle.id + end + end - it "retrieves a list of variants with appropriate attributes" do - get :managed, { :format => :json } - keys = json_response.first.keys.map{ |key| key.to_sym } - attributes.all?{ |attr| keys.include? attr }.should == true - end - end + context "as the user of some other supplier" do + before :each do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => other_supplier_user + spree_get :accessible, { :template => 'bulk_index', :format => :json } + end - context "using the accessible action to list order cycles" do - let(:oc_supplier) { create(:supplier_enterprise) } - let(:oc_distributor) { create(:distributor_enterprise) } - let(:other_supplier) { create(:supplier_enterprise) } - let(:oc_supplier_user) do - user = create(:user) - user.spree_roles = [] - user.enterprise_roles.create(enterprise: oc_supplier) - user.save! - user - end - let(:oc_distributor_user) do - user = create(:user) - user.spree_roles = [] - user.enterprise_roles.create(enterprise: oc_distributor) - user.save! - user - end - let(:other_supplier_user) do - user = create(:user) - user.spree_roles = [] - user.enterprise_roles.create(enterprise: other_supplier) - user.save! - user - end - let!(:order_cycle) { create(:simple_order_cycle, suppliers: [oc_supplier], distributors: [oc_distributor]) } + it "does not give me access" do + json_response.length.should == 0 + end + end - context "as the user of a supplier to an order cycle" do - before :each do + context "as the user of a hub for the order cycle" do + before :each do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => oc_distributor_user + spree_get :accessible, { :template => 'bulk_index', :format => :json } + end + + it "gives me access" do + json_response.length.should == 1 + json_response[0]['id'].should == order_cycle.id + end + end + end + + context "when the :as parameter is set to 'distributor'" do + let(:user) { create_enterprise_user } + let(:distributor) { create(:distributor_enterprise) } + let(:producer) { create(:supplier_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let!(:oc) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor], suppliers: [producer]) } + + let(:params) { { format: :json, as: 'distributor' } } + + before do stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => oc_supplier_user - spree_get :accessible, { :template => 'bulk_index', :format => :json } + Spree.user_class.stub :find_by_spree_api_key => user end - it "gives me access" do - json_response.length.should == 1 - json_response[0]['id'].should == order_cycle.id + context "as the manager of a supplier in an order cycle" do + before do + user.enterprise_roles.create(enterprise: producer) + spree_get :accessible, params + end + + it "does not return the order cycle" do + expect(assigns(:order_cycles)).to_not include oc + end + end + + context "as the manager of a distributor in an order cycle" do + before do + user.enterprise_roles.create(enterprise: distributor) + spree_get :accessible, params + end + + it "returns the order cycle" do + expect(assigns(:order_cycles)).to include oc + end + end + + context "as the manager of the coordinator of an order cycle" do + before do + user.enterprise_roles.create(enterprise: coordinator) + spree_get :accessible, params + end + + it "returns the order cycle" do + expect(assigns(:order_cycles)).to include oc + end end end - context "as the user of some other supplier" do - before :each do + context "when the :as parameter is set to 'producer'" do + let(:user) { create_enterprise_user } + let(:distributor) { create(:distributor_enterprise) } + let(:producer) { create(:supplier_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let!(:oc) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor], suppliers: [producer]) } + + let(:params) { { format: :json, as: 'producer' } } + + before do stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => other_supplier_user - spree_get :accessible, { :template => 'bulk_index', :format => :json } + Spree.user_class.stub :find_by_spree_api_key => user end - it "does not give me access" do - json_response.length.should == 0 - end - end + context "as the manager of a producer in an order cycle" do + before do + user.enterprise_roles.create(enterprise: producer) + spree_get :accessible, params + end - context "as the user of a hub for the order cycle" do - before :each do - stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => oc_distributor_user - spree_get :accessible, { :template => 'bulk_index', :format => :json } + it "returns the order cycle" do + expect(assigns(:order_cycles)).to include oc + end end - it "gives me access" do - json_response.length.should == 1 - json_response[0]['id'].should == order_cycle.id + context "as the manager of a distributor in an order cycle" do + before do + user.enterprise_roles.create(enterprise: distributor) + spree_get :accessible, params + end + + it "does not return the order cycle" do + expect(assigns(:order_cycles)).to_not include oc + end + end + + context "as the manager of the coordinator of an order cycle" do + before do + user.enterprise_roles.create(enterprise: coordinator) + spree_get :accessible, params + end + + it "returns the order cycle" do + expect(assigns(:order_cycles)).to include oc + end end end end diff --git a/spec/javascripts/unit/bulk_order_management_spec.js.coffee b/spec/javascripts/unit/bulk_order_management_spec.js.coffee index 61164870ed..e92f65ed5b 100644 --- a/spec/javascripts/unit/bulk_order_management_spec.js.coffee +++ b/spec/javascripts/unit/bulk_order_management_spec.js.coffee @@ -23,7 +23,7 @@ describe "AdminOrderMgmtCtrl", -> httpBackend.expectGET("/api/users/authorise_api?token=API_KEY").respond success: "Use of API Authorised" httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[is_primary_producer_eq]=true").respond returnedSuppliers httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[sells_in][]=own&q[sells_in][]=any").respond returnedDistributors - httpBackend.expectGET("/api/order_cycles/accessible").respond returnedOrderCycles + httpBackend.expectGET("/api/order_cycles/accessible?as=distributor&q[orders_close_at_gt]=SomeDate").respond returnedOrderCycles spyOn(scope, "initialiseVariables").andCallThrough() spyOn(scope, "fetchOrders").andReturn "nothing" #spyOn(returnedSuppliers, "unshift") From e640376d630d4d85c240b77f4d725ca5e26d6d15 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 30 Apr 2015 10:52:34 +1000 Subject: [PATCH 045/151] Don't load cancelled orders into bulk order management --- app/assets/javascripts/admin/bulk_order_management.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 5d36718e9a..61f275c9ed 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -60,7 +60,7 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.fetchOrders = -> $scope.loading = true - dataFetcher("/api/orders/managed?template=bulk_index;page=1;per_page=500;q[completed_at_not_null]=true;q[completed_at_gt]=#{$scope.startDate};q[completed_at_lt]=#{$scope.endDate}").then (data) -> + dataFetcher("/api/orders/managed?template=bulk_index;page=1;per_page=500;q[state_not_eq]=canceled;q[completed_at_not_null]=true;q[completed_at_gt]=#{$scope.startDate};q[completed_at_lt]=#{$scope.endDate}").then (data) -> $scope.resetOrders data $scope.loading = false From b5c7607d67bc5ccb34d5072ddd8870d989b9a254 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 30 Apr 2015 15:15:05 +1000 Subject: [PATCH 046/151] Order cycle filter resets date filters on BOM --- .../javascripts/admin/bulk_order_management.js.coffee | 7 +++++++ app/views/api/order_cycles/bulk_show.v1.rabl | 4 +++- spec/javascripts/unit/bulk_order_management_spec.js.coffee | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 61f275c9ed..420f7c3275 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -49,6 +49,8 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.distributors.unshift blankOption() ocFetcher = dataFetcher("/api/order_cycles/accessible?as=distributor&q[orders_close_at_gt]=#{formatDate(daysFromToday(-90))}").then (data) -> $scope.orderCycles = data + $scope.orderCyclesByID = [] + $scope.orderCyclesByID[oc.id] = oc for oc in $scope.orderCycles $scope.orderCycles.unshift blankOption() $scope.fetchOrders() ocFetcher.then -> @@ -162,6 +164,11 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.supplierFilter = $scope.suppliers[0].id $scope.orderCycleFilter = $scope.orderCycles[0].id $scope.quickSearch = "" + + $scope.$watch "orderCycleFilter", (newVal, oldVal) -> + unless $scope.orderCycleFilter == "0" || angular.equals(newVal, oldVal) + $scope.startDate = $scope.orderCyclesByID[$scope.orderCycleFilter].first_order + $scope.endDate = $scope.orderCyclesByID[$scope.orderCycleFilter].last_order ] daysFromToday = (days) -> diff --git a/app/views/api/order_cycles/bulk_show.v1.rabl b/app/views/api/order_cycles/bulk_show.v1.rabl index a0a5b16927..b012b879e2 100644 --- a/app/views/api/order_cycles/bulk_show.v1.rabl +++ b/app/views/api/order_cycles/bulk_show.v1.rabl @@ -1,9 +1,11 @@ object @order_cycle attributes :id, :name +node( :first_order ) { |order| order.orders_open_at.strftime("%F") } +node( :last_order ) { |order| (order.orders_close_at + 1.day).strftime("%F") } node( :suppliers ) do |oc| partial 'api/enterprises/bulk_index', :object => oc.suppliers end node( :distributors ) do |oc| partial 'api/enterprises/bulk_index', :object => oc.distributors -end \ No newline at end of file +end diff --git a/spec/javascripts/unit/bulk_order_management_spec.js.coffee b/spec/javascripts/unit/bulk_order_management_spec.js.coffee index e92f65ed5b..3377c799db 100644 --- a/spec/javascripts/unit/bulk_order_management_spec.js.coffee +++ b/spec/javascripts/unit/bulk_order_management_spec.js.coffee @@ -43,7 +43,7 @@ describe "AdminOrderMgmtCtrl", -> describe "fetching orders", -> beforeEach -> scope.initialiseVariables() - httpBackend.expectGET("/api/orders/managed?template=bulk_index;page=1;per_page=500;q[completed_at_not_null]=true;q[completed_at_gt]=SomeDate;q[completed_at_lt]=SomeDate").respond "list of orders" + httpBackend.expectGET("/api/orders/managed?template=bulk_index;page=1;per_page=500;q[state_not_eq]=canceled;q[completed_at_not_null]=true;q[completed_at_gt]=SomeDate;q[completed_at_lt]=SomeDate").respond "list of orders" it "makes a call to dataFetcher, with current start and end date parameters", -> scope.fetchOrders() From ed9bbe2c45aeb03f022bbf9c4bbaafb504f6bca7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 30 Apr 2015 15:26:54 +1000 Subject: [PATCH 047/151] Sorting Hub and Producer filter selectors by name --- .../javascripts/admin/bulk_order_management.js.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 420f7c3275..20ddc177ba 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -1,6 +1,6 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ - "$scope", "$http", "dataFetcher", "blankOption", "pendingChanges", "VariantUnitManager", "OptionValueNamer", "SpreeApiKey" - ($scope, $http, dataFetcher, blankOption, pendingChanges, VariantUnitManager, OptionValueNamer, SpreeApiKey) -> + "$scope", "$http", "$filter", "dataFetcher", "blankOption", "pendingChanges", "VariantUnitManager", "OptionValueNamer", "SpreeApiKey" + ($scope, $http, $filter, dataFetcher, blankOption, pendingChanges, VariantUnitManager, OptionValueNamer, SpreeApiKey) -> $scope.loading = true $scope.initialiseVariables = -> @@ -42,10 +42,10 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ if $scope.spree_api_key_ok $http.defaults.headers.common["X-Spree-Token"] = SpreeApiKey dataFetcher("/api/enterprises/accessible?template=bulk_index&q[is_primary_producer_eq]=true").then (data) -> - $scope.suppliers = data + $scope.suppliers = $filter('orderBy')(data, 'name') $scope.suppliers.unshift blankOption() dataFetcher("/api/enterprises/accessible?template=bulk_index&q[sells_in][]=own&q[sells_in][]=any").then (data) -> - $scope.distributors = data + $scope.distributors = $filter('orderBy')(data, 'name') $scope.distributors.unshift blankOption() ocFetcher = dataFetcher("/api/order_cycles/accessible?as=distributor&q[orders_close_at_gt]=#{formatDate(daysFromToday(-90))}").then (data) -> $scope.orderCycles = data From 28bf7037db72bf0fb790fe1965e443ce1263bf4b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 30 Apr 2015 16:20:15 +1000 Subject: [PATCH 048/151] Updating methods for retrieving allowed producers, distributors and order cycles for order and fulfillment reports --- .../spree/admin/reports_controller_decorator.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 92653a5aa9..ce714b088e 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -420,13 +420,17 @@ Spree::Admin::ReportsController.class_eval do my_distributors = Enterprise.is_distributor.managed_by(spree_current_user) my_suppliers = Enterprise.is_primary_producer.managed_by(spree_current_user) + permissions = OpenFoodNetwork::Permissions.new(spree_current_user) + # My distributors and any distributors distributing products I supply - @distributors = my_distributors | Enterprise.with_distributed_products_outer.merge(Spree::Product.in_any_supplier(my_suppliers)) + @distributors = permissions.order_cycle_enterprises.is_distributor # My suppliers and any suppliers supplying products I distribute - @suppliers = my_suppliers | my_distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq + @suppliers = permissions.order_cycle_enterprises.is_primary_producer + + @order_cycles = OrderCycle.active_or_complete. + involving_managed_distributors_of(spree_current_user).order('orders_close_at DESC') - @order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC') @report_types = REPORT_TYPES[:orders_and_fulfillment] @report_type = params[:report_type] From 0a03483e3675eea49a1fbb1f8beff15f96079f40 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 1 May 2015 12:46:20 +1000 Subject: [PATCH 049/151] Adding permissions methods for visible and editable orders and line_items --- lib/open_food_network/permissions.rb | 50 +++++++ .../lib/open_food_network/permissions_spec.rb | 131 ++++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 4edb3d5f39..59fff81a87 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -56,6 +56,51 @@ module OpenFoodNetwork permissions end + # Find enterprises that an admin is allowed to add to an order cycle + def visible_orders + # Any orders that I can edit + editable = editable_orders.pluck(:id) + + # Any orders placed through hubs that my producers have granted P-OC, and which contain my their products + # This is pretty complicated but it's looking for order where at least one of my producers has granted + # P-OC to the distributor AND the order contains products of at least one of THE SAME producers + granted_distributors = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer) + produced = Spree::Order.with_line_items_variants_and_products_outer. + where( + "spree_orders.distributor_id IN (?) AND spree_products.supplier_id IN (?)", + granted_distributors, + granting(:add_to_order_cycle, to: granted_distributors).merge(managed_enterprises.is_primary_producer) + ).pluck(:id) + + Spree::Order.where(id: editable | produced) + end + + # Find enterprises that an admin is allowed to add to an order cycle + def editable_orders + # Any orders placed through any hub that I manage + managed = Spree::Order.where(distributor_id: managed_enterprises.pluck(:id)).pluck(:id) + + # Any order that is placed through an order cycle one of my managed enterprises coordinates + coordinated = Spree::Order.where(order_cycle_id: coordinated_order_cycles.pluck(:id)).pluck(:id) + + Spree::Order.where(id: managed | coordinated ) + end + + def visible_line_items + # Any line items that I can edit + editable = editable_line_items.pluck(:id) + + # Any from visible orders, where the product is produced by one of my managed producers + produced = Spree::LineItem.where(order_id: visible_orders.pluck(:id)).joins(:product). + where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer.pluck(:id)) + + Spree::LineItem.where(id: editable | produced) + end + + def editable_line_items + Spree::LineItem.where(order_id: editable_orders) + end + def managed_products managed_enterprise_products_ids = managed_enterprise_products.pluck :id permitted_enterprise_products_ids = related_enterprise_products.pluck :id @@ -85,6 +130,11 @@ module OpenFoodNetwork @managed_enterprises = Enterprise.managed_by(@user) end + def coordinated_order_cycles + return @coordinated_order_cycles unless @coordinated_order_cycles.nil? + @coordinated_order_cycles = OrderCycle.managed_by(@user) + end + def related_enterprises_with(permission) parent_ids = EnterpriseRelationship. permitting(managed_enterprises). diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 88611b857c..d9f4608a60 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -185,5 +185,136 @@ module OpenFoodNetwork permissions.send(:related_enterprise_products).should == [p] end end + + describe "finding orders that are visible in reports" do + let(:distributor) { create(:distributor_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let(:random_enterprise) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) } + let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor ) } + let!(:line_item) { create(:line_item, order: order) } + let!(:producer) { create(:supplier_enterprise) } + + before do + permissions.stub(:coordinated_order_cycles) { Enterprise.where("1=0") } + end + + context "as the hub through which the order was placed" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: distributor) } + end + + it "should let me see the order" do + expect(permissions.visible_orders).to include order + end + end + + context "as the coordinator of the order cycle through which the order was placed" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: coordinator) } + permissions.stub(:coordinated_order_cycles) { OrderCycle.where(id: order_cycle) } + end + + it "should let me see the order" do + expect(permissions.visible_orders).to include order + end + end + + context "as a producer which has granted P-OC to the distributor of an order" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: producer) } + create(:enterprise_relationship, parent: producer, child: distributor, permissions_list: [:add_to_order_cycle]) + end + + context "which contains my products" do + before do + line_item.product.supplier = producer + line_item.product.save + end + + it "should let me see the order" do + expect(permissions.visible_orders).to include order + end + end + + context "which does not contain my products" do + it "should not let me see the order" do + expect(permissions.visible_orders).to_not include order + end + end + end + + context "as an enterprise that is a distributor in the order cycle, but not the distributor of the order" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: random_enterprise) } + end + + it "should not let me see the order" do + expect(permissions.visible_orders).to_not include order + end + end + end + + describe "finding line items that are visible in reports" do + let(:distributor) { create(:distributor_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let(:random_enterprise) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) } + let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor ) } + let!(:line_item1) { create(:line_item, order: order) } + let!(:line_item2) { create(:line_item, order: order) } + let!(:producer) { create(:supplier_enterprise) } + + before do + permissions.stub(:coordinated_order_cycles) { Enterprise.where("1=0") } + end + + context "as the hub through which the parent order was placed" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: distributor) } + end + + it "should let me see the line_items" do + expect(permissions.visible_line_items).to include line_item1, line_item2 + end + end + + context "as the coordinator of the order cycle through which the parent order was placed" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: coordinator) } + permissions.stub(:coordinated_order_cycles) { OrderCycle.where(id: order_cycle) } + end + + it "should let me see the line_items" do + expect(permissions.visible_line_items).to include line_item1, line_item2 + end + end + + context "as the manager producer which has granted P-OC to the distributor of the parent order" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: producer) } + create(:enterprise_relationship, parent: producer, child: distributor, permissions_list: [:add_to_order_cycle]) + + line_item1.product.supplier = producer + line_item1.product.save + end + + it "should let me see the line_items pertaining to variants I produce" do + ps = permissions.visible_line_items + expect(ps).to include line_item1 + expect(ps).to_not include line_item2 + end + end + + context "as an enterprise that is a distributor in the order cycle, but not the distributor of the parent order" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: random_enterprise) } + end + + it "should not let me see the line_items" do + expect(permissions.visible_line_items).to_not include line_item1, line_item2 + end + end + end end end From 4259b466f5a737b58999bb1ddaf0f861c5495177 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 2 May 2015 17:41:18 +1000 Subject: [PATCH 050/151] Using new order and line item permissions to fetch items to display in Orders and Fullfillment reports --- .../admin/reports_controller_decorator.rb | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index ce714b088e..beaee2c9be 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -406,22 +406,16 @@ Spree::Admin::ReportsController.class_eval do end params[:q][:meta_sort] ||= "completed_at.desc" - # -- Search - @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) - orders = @search.result - @line_items = orders.map do |o| - lis = o.line_items.managed_by(spree_current_user) - lis = lis.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present? - lis - end.flatten - #payments = orders.map { |o| o.payments.select { |payment| payment.completed? } }.flatten # Only select completed payments - - # -- Prepare form options - my_distributors = Enterprise.is_distributor.managed_by(spree_current_user) - my_suppliers = Enterprise.is_primary_producer.managed_by(spree_current_user) - permissions = OpenFoodNetwork::Permissions.new(spree_current_user) + # -- Search + + @search = Spree::Order.complete.not_state(:canceled).search(params[:q]) + orders = permissions.visible_orders.merge(@search.result) + + @line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders)) + @line_items = @line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present? + # My distributors and any distributors distributing products I supply @distributors = permissions.order_cycle_enterprises.is_distributor From f79fba52beab4aa818bc01a5ae7959392b1e1df1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 2 May 2015 17:43:21 +1000 Subject: [PATCH 051/151] Hiding personal details of customers, where the user does not manage the distributor of the order or the coordinator of the order cycle --- .../spree/admin/reports_controller_decorator.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index beaee2c9be..ccc142f195 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -416,6 +416,15 @@ Spree::Admin::ReportsController.class_eval do @line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders)) @line_items = @line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present? + line_items_with_hidden_details = @line_items.where("id NOT IN (?)", permissions.editable_line_items) + @line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item| + # TODO We should really be hiding customer code here too, but until we + # have an actual association between order and customer, it's a bit tricky + line_item.order.bill_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + line_item.order.ship_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + line_item.order.assign_attributes(email: "HIDDEN") + end + # My distributors and any distributors distributing products I supply @distributors = permissions.order_cycle_enterprises.is_distributor From 7ffe0f042ee7e925d3a81214c54e54b7138395e0 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 11:48:39 +1000 Subject: [PATCH 052/151] Moving accessible_by scope on Enterprise to permissions --- app/controllers/api/enterprises_controller.rb | 3 +- app/models/enterprise.rb | 9 ------ lib/open_food_network/permissions.rb | 13 +++++++++ .../lib/open_food_network/permissions_spec.rb | 12 ++++++-- spec/models/enterprise_spec.rb | 28 ------------------- 5 files changed, 24 insertions(+), 41 deletions(-) diff --git a/app/controllers/api/enterprises_controller.rb b/app/controllers/api/enterprises_controller.rb index 468f3a22ad..973492b5e7 100644 --- a/app/controllers/api/enterprises_controller.rb +++ b/app/controllers/api/enterprises_controller.rb @@ -13,7 +13,8 @@ module Api end def accessible - @enterprises = Enterprise.ransack(params[:q]).result.accessible_by(current_api_user) + permitted = Permissions.new(current_api_user).enterprises_managed_or_granting_add_to_order_cycle + @enterprises = permitted.ransack(params[:q]).result render params[:template] || :bulk_index end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 3cf00730a8..e921167cf0 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -162,15 +162,6 @@ class Enterprise < ActiveRecord::Base end } - # Return enterprises that the user manages and those that have granted P-OC to managed enterprises - scope :accessible_by, lambda { |user| - if user.has_spree_role?('admin') - scoped - else - where(id: OpenFoodNetwork::Permissions.new(user).order_cycle_enterprises) - end - } - def self.find_near(suburb) enterprises = [] diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 59fff81a87..6b6ef85405 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -15,6 +15,15 @@ module OpenFoodNetwork managed_and_related_enterprises_with :add_to_order_cycle end + def enterprises_managed_or_granting_add_to_order_cycle + # Return enterprises that the user manages and those that have granted P-OC to managed enterprises + if admin? + Enterprise.scoped + else + managed_and_related_enterprises_with :add_to_order_cycle + end + end + # Find enterprises for which an admin is allowed to edit their profile def editable_enterprises managed_and_related_enterprises_with :edit_profile @@ -118,6 +127,10 @@ module OpenFoodNetwork private + def admin? + @user.admin? + end + def managed_and_related_enterprises_with(permission) managed_enterprise_ids = managed_enterprises.pluck :id permitting_enterprise_ids = related_enterprises_with(permission).pluck :id diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index d9f4608a60..e3262949ee 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -12,12 +12,18 @@ module OpenFoodNetwork let(:e) { double(:enterprise) } it "returns managed and related enterprises with add_to_order_cycle permission" do - permissions. - should_receive(:managed_and_related_enterprises_with). + allow(user).to receive(:admin?) { false } + expect(permissions).to receive(:managed_and_related_enterprises_with). with(:add_to_order_cycle). and_return([e]) - permissions.order_cycle_enterprises.should == [e] + expect(permissions.enterprises_managed_or_granting_add_to_order_cycle).to eq [e] + end + + it "shows all enterprises for admin user" do + allow(user).to receive(:admin?) { true } + + expect(permissions.enterprises_managed_or_granting_add_to_order_cycle).to eq [e1, e2] end end diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index df5dbbfbc7..df1b58aca6 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -558,34 +558,6 @@ describe Enterprise do enterprises.should include e2 end end - - describe "accessible_by" do - it "shows only managed enterprises and enterprises granting them P-OC" do - user = create(:user) - user.spree_roles = [] - e1 = create(:enterprise) - e2 = create(:enterprise) - e3 = create(:enterprise) - e1.enterprise_roles.build(user: user).save - create(:enterprise_relationship, parent: e2, child: e1, permissions_list: [:add_to_order_cycle]) - - enterprises = Enterprise.accessible_by user - enterprises.length.should == 2 - enterprises.should include e1, e2 - enterprises.should_not include e3 - end - - it "shows all enterprises for admin user" do - user = create(:admin_user) - e1 = create(:enterprise) - e2 = create(:enterprise) - - enterprises = Enterprise.managed_by user - enterprises.length.should == 2 - enterprises.should include e1 - enterprises.should include e2 - end - end end describe "callbacks" do From f0f7e0ee2f164a725d88928e4e9ec9d656d11842 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 12:03:52 +1000 Subject: [PATCH 053/151] Making permissions method managed_and_related_enterprise_with method more specific --- lib/open_food_network/permissions.rb | 16 ++++++++-------- spec/lib/open_food_network/permissions_spec.rb | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 6b6ef85405..953314c8f8 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -5,14 +5,14 @@ module OpenFoodNetwork end def can_manage_complex_order_cycles? - managed_and_related_enterprises_with(:add_to_order_cycle).any? do |e| + managed_and_related_enterprises_granting(:add_to_order_cycle).any? do |e| e.sells == 'any' end end # Find enterprises that an admin is allowed to add to an order cycle def order_cycle_enterprises - managed_and_related_enterprises_with :add_to_order_cycle + managed_and_related_enterprises_granting :add_to_order_cycle end def enterprises_managed_or_granting_add_to_order_cycle @@ -20,17 +20,17 @@ module OpenFoodNetwork if admin? Enterprise.scoped else - managed_and_related_enterprises_with :add_to_order_cycle + managed_and_related_enterprises_granting :add_to_order_cycle end end # Find enterprises for which an admin is allowed to edit their profile def editable_enterprises - managed_and_related_enterprises_with :edit_profile + managed_and_related_enterprises_granting :edit_profile end def variant_override_hubs - managed_and_related_enterprises_with(:add_to_order_cycle).is_hub + managed_and_related_enterprises_granting(:add_to_order_cycle).is_hub end def variant_override_producers @@ -42,7 +42,7 @@ module OpenFoodNetwork # override variants # {hub1_id => [producer1_id, producer2_id, ...], ...} def variant_override_enterprises_per_hub - hubs = managed_and_related_enterprises_with(:add_to_order_cycle).is_distributor + hubs = managed_and_related_enterprises_granting(:add_to_order_cycle).is_distributor # Permissions granted by create_variant_overrides relationship from producer to hub permissions = Hash[ @@ -117,7 +117,7 @@ module OpenFoodNetwork end def managed_product_enterprises - managed_and_related_enterprises_with :manage_products + managed_and_related_enterprises_granting :manage_products end def manages_one_enterprise? @@ -131,7 +131,7 @@ module OpenFoodNetwork @user.admin? end - def managed_and_related_enterprises_with(permission) + def managed_and_related_enterprises_granting(permission) managed_enterprise_ids = managed_enterprises.pluck :id permitting_enterprise_ids = related_enterprises_with(permission).pluck :id diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index e3262949ee..6c89dc3db1 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -13,7 +13,7 @@ module OpenFoodNetwork it "returns managed and related enterprises with add_to_order_cycle permission" do allow(user).to receive(:admin?) { false } - expect(permissions).to receive(:managed_and_related_enterprises_with). + expect(permissions).to receive(:managed_and_related_enterprises_granting). with(:add_to_order_cycle). and_return([e]) @@ -32,7 +32,7 @@ module OpenFoodNetwork it "returns managed and related enterprises with edit_profile permission" do permissions. - should_receive(:managed_and_related_enterprises_with). + should_receive(:managed_and_related_enterprises_granting). with(:edit_profile). and_return([e]) @@ -139,7 +139,7 @@ module OpenFoodNetwork it "returns managed and related enterprises with manage_products permission" do permissions. - should_receive(:managed_and_related_enterprises_with). + should_receive(:managed_and_related_enterprises_granting). with(:manage_products). and_return([e]) @@ -171,13 +171,13 @@ module OpenFoodNetwork it "returns managed enterprises" do permissions.should_receive(:managed_enterprises) { Enterprise.where(id: e1) } - permissions.send(:managed_and_related_enterprises_with, permission).should == [e1] + permissions.send(:managed_and_related_enterprises_granting, permission).should == [e1] end it "returns permitted enterprises" do permissions.should_receive(:related_enterprises_with).with(permission). and_return(Enterprise.where(id: e2)) - permissions.send(:managed_and_related_enterprises_with, permission).should == [e2] + permissions.send(:managed_and_related_enterprises_granting, permission).should == [e2] end end From 5cd528a87d8ce669ca5ed33052bddcc732b89ed7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 12:09:54 +1000 Subject: [PATCH 054/151] Removing obsolete related_enterprises_with permission method --- lib/open_food_network/permissions.rb | 13 ++----------- spec/lib/open_food_network/permissions_spec.rb | 10 +++++----- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 953314c8f8..3c6526ff30 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -133,7 +133,7 @@ module OpenFoodNetwork def managed_and_related_enterprises_granting(permission) managed_enterprise_ids = managed_enterprises.pluck :id - permitting_enterprise_ids = related_enterprises_with(permission).pluck :id + permitting_enterprise_ids = granting(permission).pluck :id Enterprise.where('id IN (?)', managed_enterprise_ids + permitting_enterprise_ids) end @@ -148,15 +148,6 @@ module OpenFoodNetwork @coordinated_order_cycles = OrderCycle.managed_by(@user) end - def related_enterprises_with(permission) - parent_ids = EnterpriseRelationship. - permitting(managed_enterprises). - with_permission(permission). - pluck(:parent_id) - - Enterprise.where('id IN (?)', parent_ids) - end - def granting(permission, options={}) parent_ids = EnterpriseRelationship. permitting(options[:to] || managed_enterprises). @@ -180,7 +171,7 @@ module OpenFoodNetwork end def related_enterprise_products - Spree::Product.where('supplier_id IN (?)', related_enterprises_with(:manage_products)) + Spree::Product.where('supplier_id IN (?)', granting(:manage_products)) end end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 6c89dc3db1..bc26b2da31 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -154,19 +154,19 @@ module OpenFoodNetwork it "returns the enterprises" do permissions.stub(:managed_enterprises) { e2 } - permissions.send(:related_enterprises_with, permission).should == [e1] + permissions.send(:granting, permission).should == [e1] end it "returns an empty array when there are none" do permissions.stub(:managed_enterprises) { e1 } - permissions.send(:related_enterprises_with, permission).should == [] + permissions.send(:granting, permission).should == [] end end describe "finding enterprises that are managed or with a particular permission" do before do permissions.stub(:managed_enterprises) { Enterprise.where('1=0') } - permissions.stub(:related_enterprises_with) { Enterprise.where('1=0') } + permissions.stub(:granting) { Enterprise.where('1=0') } end it "returns managed enterprises" do @@ -175,7 +175,7 @@ module OpenFoodNetwork end it "returns permitted enterprises" do - permissions.should_receive(:related_enterprises_with).with(permission). + permissions.should_receive(:granting).with(permission). and_return(Enterprise.where(id: e2)) permissions.send(:managed_and_related_enterprises_granting, permission).should == [e2] end @@ -186,7 +186,7 @@ module OpenFoodNetwork let!(:p) { create(:simple_product, supplier: e) } it "returns supplied products" do - permissions.should_receive(:related_enterprises_with).with(:manage_products) { [e] } + permissions.should_receive(:granting).with(:manage_products) { [e] } permissions.send(:related_enterprise_products).should == [p] end From 5806f50a849e8c6958d855a62095d5cae93aa238 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 12:14:23 +1000 Subject: [PATCH 055/151] Renaming granting > related_enterprises_granting --- .../order_cycle_permissions.rb | 18 +++++++++--------- lib/open_food_network/permissions.rb | 8 ++++---- spec/lib/open_food_network/permissions_spec.rb | 10 +++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index 2af60c5696..14ac9a68be 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -19,7 +19,7 @@ module OpenFoodNetwork if @coordinator.sells == "any" # If the coordinator sells any, relationships come into play - granting(:add_to_order_cycle, to: [@coordinator]).pluck(:id).each do |enterprise_id| + related_enterprises_granting(:add_to_order_cycle, to: [@coordinator]).pluck(:id).each do |enterprise_id| coordinator_permitted << enterprise_id end @@ -30,19 +30,19 @@ module OpenFoodNetwork Enterprise.where(id: coordinator_permitted | all_active) else # Any enterprises that I manage directly, which have granted P-OC to the coordinator - managed_permitted = granting(:add_to_order_cycle, to: [@coordinator], scope: managed_participating_enterprises ).pluck(:id) + managed_permitted = related_enterprises_granting(:add_to_order_cycle, to: [@coordinator], scope: managed_participating_enterprises ).pluck(:id) # Any hubs in this OC that have been granted P-OC by producers I manage in this OC hubs_permitted = granted(:add_to_order_cycle, by: managed_participating_producers, scope: @order_cycle.distributors).pluck(:id) # Any hubs in this OC that have granted P-OC to producers I manage in this OC - hubs_permitting = granting(:add_to_order_cycle, to: managed_participating_producers, scope: @order_cycle.distributors).pluck(:id) + hubs_permitting = related_enterprises_granting(:add_to_order_cycle, to: managed_participating_producers, scope: @order_cycle.distributors).pluck(:id) # Any producers in this OC that have been granted P-OC by hubs I manage in this OC producers_permitted = granted(:add_to_order_cycle, by: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) # Any producers in this OC that have granted P-OC to hubs I manage in this OC - producers_permitting = granting(:add_to_order_cycle, to: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) + producers_permitting = related_enterprises_granting(:add_to_order_cycle, to: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) managed_active = [] hubs_active = [] @@ -125,7 +125,7 @@ module OpenFoodNetwork end # Any variants of any producers that have granted the hub P-OC - producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) + producers = related_enterprises_granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break @@ -138,7 +138,7 @@ module OpenFoodNetwork Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) else # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub - producers = granting(:add_to_order_cycle, to: [hub], scope: managed_participating_producers) + producers = related_enterprises_granting(:add_to_order_cycle, to: [hub], scope: managed_participating_producers) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break @@ -162,7 +162,7 @@ module OpenFoodNetwork end # Any variants of any producers that have granted the hub P-OC - producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) + producers = related_enterprises_granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break @@ -178,7 +178,7 @@ module OpenFoodNetwork granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_participating_producers) # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub - granting_producers = granting(:add_to_order_cycle, to: [hub], scope: granted_producers) + granting_producers = related_enterprises_granting(:add_to_order_cycle, to: [hub], scope: granted_producers) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', granting_producers) Spree::Variant.where(id: permitted_variants) @@ -216,7 +216,7 @@ module OpenFoodNetwork # Find my managed hubs in this order cycle hubs = managed_participating_hubs # Any incoming exchange where the producer has granted P-OC to one or more of those hubs - producers = granting(:add_to_order_cycle, to: hubs, scope: Enterprise.is_primary_producer).pluck :id + producers = related_enterprises_granting(:add_to_order_cycle, to: hubs, scope: Enterprise.is_primary_producer).pluck :id permitted_exchanges = @order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id # TODO: remove active_exchanges when we think it is safe to do so diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 3c6526ff30..296ab9843c 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -78,7 +78,7 @@ module OpenFoodNetwork where( "spree_orders.distributor_id IN (?) AND spree_products.supplier_id IN (?)", granted_distributors, - granting(:add_to_order_cycle, to: granted_distributors).merge(managed_enterprises.is_primary_producer) + related_enterprises_granting(:add_to_order_cycle, to: granted_distributors).merge(managed_enterprises.is_primary_producer) ).pluck(:id) Spree::Order.where(id: editable | produced) @@ -133,7 +133,7 @@ module OpenFoodNetwork def managed_and_related_enterprises_granting(permission) managed_enterprise_ids = managed_enterprises.pluck :id - permitting_enterprise_ids = granting(permission).pluck :id + permitting_enterprise_ids = related_enterprises_granting(permission).pluck :id Enterprise.where('id IN (?)', managed_enterprise_ids + permitting_enterprise_ids) end @@ -148,7 +148,7 @@ module OpenFoodNetwork @coordinated_order_cycles = OrderCycle.managed_by(@user) end - def granting(permission, options={}) + def related_enterprises_granting(permission, options={}) parent_ids = EnterpriseRelationship. permitting(options[:to] || managed_enterprises). with_permission(permission). @@ -171,7 +171,7 @@ module OpenFoodNetwork end def related_enterprise_products - Spree::Product.where('supplier_id IN (?)', granting(:manage_products)) + Spree::Product.where('supplier_id IN (?)', related_enterprises_granting(:manage_products)) end end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index bc26b2da31..3d796b9f46 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -154,19 +154,19 @@ module OpenFoodNetwork it "returns the enterprises" do permissions.stub(:managed_enterprises) { e2 } - permissions.send(:granting, permission).should == [e1] + permissions.send(:related_enterprises_granting, permission).should == [e1] end it "returns an empty array when there are none" do permissions.stub(:managed_enterprises) { e1 } - permissions.send(:granting, permission).should == [] + permissions.send(:related_enterprises_granting, permission).should == [] end end describe "finding enterprises that are managed or with a particular permission" do before do permissions.stub(:managed_enterprises) { Enterprise.where('1=0') } - permissions.stub(:granting) { Enterprise.where('1=0') } + permissions.stub(:related_enterprises_granting) { Enterprise.where('1=0') } end it "returns managed enterprises" do @@ -175,7 +175,7 @@ module OpenFoodNetwork end it "returns permitted enterprises" do - permissions.should_receive(:granting).with(permission). + permissions.should_receive(:related_enterprises_granting).with(permission). and_return(Enterprise.where(id: e2)) permissions.send(:managed_and_related_enterprises_granting, permission).should == [e2] end @@ -186,7 +186,7 @@ module OpenFoodNetwork let!(:p) { create(:simple_product, supplier: e) } it "returns supplied products" do - permissions.should_receive(:granting).with(:manage_products) { [e] } + permissions.should_receive(:related_enterprises_granting).with(:manage_products) { [e] } permissions.send(:related_enterprise_products).should == [p] end From d8f5669fbbf7e7bfb38c1fa1ac7696b0c0488b98 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 12:18:15 +1000 Subject: [PATCH 056/151] Renaming granted > related_enterprises_granted --- lib/open_food_network/order_cycle_permissions.rb | 8 ++++---- lib/open_food_network/permissions.rb | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index 14ac9a68be..08fe7b2ee4 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -33,13 +33,13 @@ module OpenFoodNetwork managed_permitted = related_enterprises_granting(:add_to_order_cycle, to: [@coordinator], scope: managed_participating_enterprises ).pluck(:id) # Any hubs in this OC that have been granted P-OC by producers I manage in this OC - hubs_permitted = granted(:add_to_order_cycle, by: managed_participating_producers, scope: @order_cycle.distributors).pluck(:id) + hubs_permitted = related_enterprises_granted(:add_to_order_cycle, by: managed_participating_producers, scope: @order_cycle.distributors).pluck(:id) # Any hubs in this OC that have granted P-OC to producers I manage in this OC hubs_permitting = related_enterprises_granting(:add_to_order_cycle, to: managed_participating_producers, scope: @order_cycle.distributors).pluck(:id) # Any producers in this OC that have been granted P-OC by hubs I manage in this OC - producers_permitted = granted(:add_to_order_cycle, by: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) + producers_permitted = related_enterprises_granted(:add_to_order_cycle, by: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) # Any producers in this OC that have granted P-OC to hubs I manage in this OC producers_permitting = related_enterprises_granting(:add_to_order_cycle, to: managed_participating_hubs, scope: @order_cycle.suppliers).pluck(:id) @@ -175,7 +175,7 @@ module OpenFoodNetwork Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) else # Any of my managed producers in this order cycle granted P-OC by the hub - granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_participating_producers) + granted_producers = related_enterprises_granted(:add_to_order_cycle, by: [hub], scope: managed_participating_producers) # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub granting_producers = related_enterprises_granting(:add_to_order_cycle, to: [hub], scope: granted_producers) @@ -235,7 +235,7 @@ module OpenFoodNetwork # Find my producers in this order cycle producers = managed_participating_producers.pluck :id # Any outgoing exchange where the distributor has been granted P-OC by one or more of those producers - hubs = granted(:add_to_order_cycle, by: producers, scope: Enterprise.is_hub) + hubs = related_enterprises_granted(:add_to_order_cycle, by: producers, scope: Enterprise.is_hub) permitted_exchanges = @order_cycle.exchanges.outgoing.where(receiver_id: hubs).pluck :id # TODO: remove active_exchanges when we think it is safe to do so diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 296ab9843c..ea4d58208e 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -73,7 +73,7 @@ module OpenFoodNetwork # Any orders placed through hubs that my producers have granted P-OC, and which contain my their products # This is pretty complicated but it's looking for order where at least one of my producers has granted # P-OC to the distributor AND the order contains products of at least one of THE SAME producers - granted_distributors = granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer) + granted_distributors = related_enterprises_granted(:add_to_order_cycle, by: managed_enterprises.is_primary_producer) produced = Spree::Order.with_line_items_variants_and_products_outer. where( "spree_orders.distributor_id IN (?) AND spree_products.supplier_id IN (?)", @@ -157,7 +157,7 @@ module OpenFoodNetwork (options[:scope] || Enterprise).where('enterprises.id IN (?)', parent_ids) end - def granted(permission, options={}) + def related_enterprises_granted(permission, options={}) child_ids = EnterpriseRelationship. permitted_by(options[:by] || managed_enterprises). with_permission(permission). From bd66091d75a18104f121ddd6288f9b169c234614 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 12:49:07 +1000 Subject: [PATCH 057/151] Push logic for checking of user super admin status down into private method --- .../admin/reports_controller_decorator.rb | 4 +-- lib/open_food_network/permissions.rb | 16 +++++----- .../lib/open_food_network/permissions_spec.rb | 30 ++++++++++++++----- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index ccc142f195..69165da484 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -426,10 +426,10 @@ Spree::Admin::ReportsController.class_eval do end # My distributors and any distributors distributing products I supply - @distributors = permissions.order_cycle_enterprises.is_distributor + @distributors = permissions.order_report_enterprises(:add_to_order_cycle).is_distributor # My suppliers and any suppliers supplying products I distribute - @suppliers = permissions.order_cycle_enterprises.is_primary_producer + @suppliers = permissions.order_report_enterprises(:add_to_order_cycle).is_primary_producer @order_cycles = OrderCycle.active_or_complete. involving_managed_distributors_of(spree_current_user).order('orders_close_at DESC') diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index ea4d58208e..d7126263d5 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -17,11 +17,7 @@ module OpenFoodNetwork def enterprises_managed_or_granting_add_to_order_cycle # Return enterprises that the user manages and those that have granted P-OC to managed enterprises - if admin? - Enterprise.scoped - else - managed_and_related_enterprises_granting :add_to_order_cycle - end + managed_and_related_enterprises_granting :add_to_order_cycle end # Find enterprises for which an admin is allowed to edit their profile @@ -132,10 +128,14 @@ module OpenFoodNetwork end def managed_and_related_enterprises_granting(permission) - managed_enterprise_ids = managed_enterprises.pluck :id - permitting_enterprise_ids = related_enterprises_granting(permission).pluck :id + if admin? + Enterprise.scoped + else + managed_enterprise_ids = managed_enterprises.pluck :id + permitting_enterprise_ids = related_enterprises_granting(permission).pluck :id - Enterprise.where('id IN (?)', managed_enterprise_ids + permitting_enterprise_ids) + Enterprise.where('id IN (?)', managed_enterprise_ids + permitting_enterprise_ids) + end end def managed_enterprises diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 3d796b9f46..de22db9a59 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -8,23 +8,37 @@ module OpenFoodNetwork let(:e1) { create(:enterprise) } let(:e2) { create(:enterprise) } + describe "finding managed and related enterprises granting a particular permission" do + describe "as super admin" do + before { allow(user).to receive(:admin?) { true } } + + it "returns all enterprises" do + expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to eq [e1, e2] + end + end + + describe "as an enterprise user" do + let(:e3) { create(:enterprise) } + before { allow(user).to receive(:admin?) { false } } + + it "returns only my managed enterprises any that have granting them P-OC" do + expect(permissions).to receive(:managed_enterprises) { Enterprise.where(id: e1) } + expect(permissions).to receive(:related_enterprises_granting).with(:some_permission) { Enterprise.where(id: e3) } + expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to eq [e1, e3] + end + end + end + describe "finding enterprises that can be added to an order cycle" do let(:e) { double(:enterprise) } it "returns managed and related enterprises with add_to_order_cycle permission" do - allow(user).to receive(:admin?) { false } expect(permissions).to receive(:managed_and_related_enterprises_granting). with(:add_to_order_cycle). and_return([e]) expect(permissions.enterprises_managed_or_granting_add_to_order_cycle).to eq [e] end - - it "shows all enterprises for admin user" do - allow(user).to receive(:admin?) { true } - - expect(permissions.enterprises_managed_or_granting_add_to_order_cycle).to eq [e1, e2] - end end describe "finding enterprises whose profiles can be edited" do @@ -61,6 +75,7 @@ module OpenFoodNetwork before do permissions.stub(:managed_enterprises) { Enterprise.where(id: hub.id) } + permissions.stub(:admin?) { false } end it "returns enterprises as hub_id => [producer, ...]" do @@ -167,6 +182,7 @@ module OpenFoodNetwork before do permissions.stub(:managed_enterprises) { Enterprise.where('1=0') } permissions.stub(:related_enterprises_granting) { Enterprise.where('1=0') } + permissions.stub(:admin?) { false } end it "returns managed enterprises" do From a7019e7e7829b212dc86bfb95a28274de5be2b6e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 13:00:36 +1000 Subject: [PATCH 058/151] Adding permissions method for order report enterprises --- app/controllers/api/enterprises_controller.rb | 2 +- .../admin/reports_controller_decorator.rb | 4 +- lib/open_food_network/permissions.rb | 17 +++++++-- .../lib/open_food_network/permissions_spec.rb | 37 ++++++++++++++++++- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/enterprises_controller.rb b/app/controllers/api/enterprises_controller.rb index 973492b5e7..f39c34d22e 100644 --- a/app/controllers/api/enterprises_controller.rb +++ b/app/controllers/api/enterprises_controller.rb @@ -13,7 +13,7 @@ module Api end def accessible - permitted = Permissions.new(current_api_user).enterprises_managed_or_granting_add_to_order_cycle + permitted = Permissions.new(current_api_user).order_cycle_enterprises @enterprises = permitted.ransack(params[:q]).result render params[:template] || :bulk_index end diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 69165da484..3aa4f560a1 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -426,10 +426,10 @@ Spree::Admin::ReportsController.class_eval do end # My distributors and any distributors distributing products I supply - @distributors = permissions.order_report_enterprises(:add_to_order_cycle).is_distributor + @distributors = permissions.visible_enterprises_for_order_reports.is_distributor # My suppliers and any suppliers supplying products I distribute - @suppliers = permissions.order_report_enterprises(:add_to_order_cycle).is_primary_producer + @suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer @order_cycles = OrderCycle.active_or_complete. involving_managed_distributors_of(spree_current_user).order('orders_close_at DESC') diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index d7126263d5..4da645582d 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -11,11 +11,11 @@ module OpenFoodNetwork end # Find enterprises that an admin is allowed to add to an order cycle - def order_cycle_enterprises - managed_and_related_enterprises_granting :add_to_order_cycle + def visible_enterprises_for_order_reports + managed_and_related_enterprises_with :add_to_order_cycle end - def enterprises_managed_or_granting_add_to_order_cycle + def order_cycle_enterprises # Return enterprises that the user manages and those that have granted P-OC to managed enterprises managed_and_related_enterprises_granting :add_to_order_cycle end @@ -138,6 +138,17 @@ module OpenFoodNetwork end end + def managed_and_related_enterprises_with(permission) + if admin? + Enterprise.scoped + else + managed = managed_enterprises.pluck(:id) + granting = related_enterprises_granting(permission).pluck(:id) + granted = related_enterprises_granted(permission).pluck(:id) + Enterprise.where(id: managed | granting | granted) + end + end + def managed_enterprises return @managed_enterprises unless @managed_enterprises.nil? @managed_enterprises = Enterprise.managed_by(@user) diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index de22db9a59..10552572f0 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -29,6 +29,41 @@ module OpenFoodNetwork end end + describe "finding managed and related enterprises granting or granted a particular permission" do + describe "as super admin" do + before { allow(user).to receive(:admin?) { true } } + + it "returns all enterprises" do + expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to eq [e1, e2] + end + end + + describe "as an enterprise user" do + let(:e3) { create(:enterprise) } + let(:e4) { create(:enterprise) } + before { allow(user).to receive(:admin?) { false } } + + it "returns only my managed enterprises any that have granting them P-OC" do + expect(permissions).to receive(:managed_enterprises) { Enterprise.where(id: e1) } + expect(permissions).to receive(:related_enterprises_granting).with(:some_permission) { Enterprise.where(id: e3) } + expect(permissions).to receive(:related_enterprises_granted).with(:some_permission) { Enterprise.where(id: e4) } + expect(permissions.send(:managed_and_related_enterprises_with, :some_permission)).to eq [e1, e3, e4] + end + end + end + + describe "finding enterprises that can be selected in order report filters" do + let(:e) { double(:enterprise) } + + it "returns managed and related enterprises with add_to_order_cycle permission" do + expect(permissions).to receive(:managed_and_related_enterprises_with). + with(:add_to_order_cycle). + and_return([e]) + + expect(permissions.visible_enterprises_for_order_reports).to eq [e] + end + end + describe "finding enterprises that can be added to an order cycle" do let(:e) { double(:enterprise) } @@ -37,7 +72,7 @@ module OpenFoodNetwork with(:add_to_order_cycle). and_return([e]) - expect(permissions.enterprises_managed_or_granting_add_to_order_cycle).to eq [e] + expect(permissions.order_cycle_enterprises).to eq [e] end end From f88fdac710e637d18e4c3251c05a3df9767e65ba Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 13:53:59 +1000 Subject: [PATCH 059/151] Adding module - doh! --- app/controllers/api/enterprises_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/enterprises_controller.rb b/app/controllers/api/enterprises_controller.rb index f39c34d22e..41b24f30a1 100644 --- a/app/controllers/api/enterprises_controller.rb +++ b/app/controllers/api/enterprises_controller.rb @@ -13,7 +13,7 @@ module Api end def accessible - permitted = Permissions.new(current_api_user).order_cycle_enterprises + permitted = OpenFoodNetwork::Permissions.new(current_api_user).order_cycle_enterprises @enterprises = permitted.ransack(params[:q]).result render params[:template] || :bulk_index end From 68b4cb59be1cce148b663f806e921eb4a9fffce1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 14:46:23 +1000 Subject: [PATCH 060/151] Fixing bulk management specs broken by making order_cycles filter update dates --- .../admin/bulk_order_management_spec.rb | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index c8ad230a00..1058bebfc0 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -238,8 +238,9 @@ feature %q{ end context "order_cycle filter" do - let!(:oc1) { FactoryGirl.create(:simple_order_cycle ) } - let!(:oc2) { FactoryGirl.create(:simple_order_cycle ) } + let!(:distributor) { create(:distributor_enterprise) } + let!(:oc1) { FactoryGirl.create(:simple_order_cycle, distributors: [distributor]) } + let!(:oc2) { FactoryGirl.create(:simple_order_cycle, distributors: [distributor]) } let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, order_cycle: oc1 ) } let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, order_cycle: oc2 ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } @@ -255,20 +256,22 @@ feature %q{ find("div.select2-container#s2id_order_cycle_filter").click order_cycle_names.each { |ocn| page.should have_selector "div.select2-drop-active ul.select2-results li", text: ocn } find("div.select2-container#s2id_order_cycle_filter").click - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + page.should have_selector "tr#li_#{li1.id}" + page.should have_selector "tr#li_#{li2.id}" select2_select oc1.name, from: "order_cycle_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + page.should have_selector "#loading img.spinner" + page.should_not have_selector "#loading img.spinner" + page.should have_selector "tr#li_#{li1.id}" + page.should_not have_selector "tr#li_#{li2.id}" end it "displays all line items when 'All' is selected from order_cycle filter" do select2_select oc1.name, from: "order_cycle_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + page.should have_selector "tr#li_#{li1.id}" + page.should_not have_selector "tr#li_#{li2.id}" select2_select "All", from: "order_cycle_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + page.should have_selector "tr#li_#{li1.id}" + page.should have_selector "tr#li_#{li2.id}" end end From 0d5ce5ff57a804a76e380a41f60c2078b524ad49 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 May 2015 15:19:48 +1000 Subject: [PATCH 061/151] Fixing issues with reports controller spec --- .../spree/admin/reports_controller_spec.rb | 83 ++++++++++++------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/spec/controllers/spree/admin/reports_controller_spec.rb b/spec/controllers/spree/admin/reports_controller_spec.rb index 432bc4e67f..58d406519b 100644 --- a/spec/controllers/spree/admin/reports_controller_spec.rb +++ b/spec/controllers/spree/admin/reports_controller_spec.rb @@ -1,23 +1,25 @@ require 'spec_helper' describe Spree::Admin::ReportsController do - + # Given two distributors and two suppliers let(:ba) { create(:address) } let(:si) { "pick up on thursday please" } - let(:s1) { create(:supplier_enterprise, address: create(:address)) } - let(:s2) { create(:supplier_enterprise, address: create(:address)) } - let(:s3) { create(:supplier_enterprise, address: create(:address)) } - let(:d1) { create(:distributor_enterprise, address: create(:address)) } - let(:d2) { create(:distributor_enterprise, address: create(:address)) } - let(:d3) { create(:distributor_enterprise, address: create(:address)) } + let(:c1) { create(:distributor_enterprise) } + let(:c2) { create(:distributor_enterprise) } + let(:s1) { create(:supplier_enterprise) } + let(:s2) { create(:supplier_enterprise) } + let(:s3) { create(:supplier_enterprise) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let(:d3) { create(:distributor_enterprise) } let(:p1) { create(:product, price: 12.34, distributors: [d1], supplier: s1) } let(:p2) { create(:product, price: 23.45, distributors: [d2], supplier: s2) } let(:p3) { create(:product, price: 34.56, distributors: [d3], supplier: s3) } # Given two order cycles with both distributors - let(:ocA) { create(:simple_order_cycle, distributors: [d1, d2], suppliers: [s1, s2, s3], variants: [p1.master, p3.master]) } - let(:ocB) { create(:simple_order_cycle, distributors: [d1, d2], suppliers: [s1, s2, s3], variants: [p2.master]) } + let(:ocA) { create(:simple_order_cycle, coordinator: c1, distributors: [d1, d2], suppliers: [s1, s2, s3], variants: [p1.master, p3.master]) } + let(:ocB) { create(:simple_order_cycle, coordinator: c2, distributors: [d1, d2], suppliers: [s1, s2, s3], variants: [p2.master]) } # orderA1 can only be accessed by s1, s3 and d1 let!(:orderA1) do @@ -53,15 +55,29 @@ describe Spree::Admin::ReportsController do order.save order end - - # As a Distributor Enterprise user for d1 + + # As manager of a coordinator (c1) + context "Coordinator Enterprise User" do + before { login_as_enterprise_user [c1] } + + describe 'Orders & Fulfillment' do + it "shows all orders in order cycles I coordinate" do + spree_get :orders_and_fulfillment + + assigns(:line_items).map(&:order).should include orderA1, orderA2 + assigns(:line_items).map(&:order).should_not include orderB1, orderB2 + end + end + end + + # As a Distributor Enterprise user for d1 context "Distributor Enterprise User" do before { login_as_enterprise_user [d1] } describe 'Orders and Distributors' do it "only shows orders that I have access to" do spree_get :orders_and_distributors - + assigns(:search).result.should include(orderA1, orderB1) assigns(:search).result.should_not include(orderA2) assigns(:search).result.should_not include(orderB2) @@ -71,7 +87,7 @@ describe Spree::Admin::ReportsController do describe 'Bulk Coop' do it "only shows orders that I have access to" do spree_get :bulk_coop - + assigns(:search).result.should include(orderA1, orderB1) assigns(:search).result.should_not include(orderA2) assigns(:search).result.should_not include(orderB2) @@ -81,7 +97,7 @@ describe Spree::Admin::ReportsController do describe 'Payments' do it "only shows orders that I have access to" do spree_get :payments - + assigns(:search).result.should include(orderA1, orderB1) assigns(:search).result.should_not include(orderA2) assigns(:search).result.should_not include(orderB2) @@ -89,12 +105,11 @@ describe Spree::Admin::ReportsController do end describe 'Orders & Fulfillment' do - it "only shows orders that I have access to" do + it "only shows orders that I distribute" do spree_get :orders_and_fulfillment - assigns(:search).result.should include(orderA1, orderB1) - assigns(:search).result.should_not include(orderA2) - assigns(:search).result.should_not include(orderB2) + assigns(:line_items).map(&:order).should include orderA1, orderB1 + assigns(:line_items).map(&:order).should_not include orderA2, orderB2 end it "only shows the selected order cycle" do @@ -114,19 +129,31 @@ describe Spree::Admin::ReportsController do it "only shows product line items that I am supplying" do spree_get :bulk_coop - assigns(:line_items).map(&:product).should include(p1) - assigns(:line_items).map(&:product).should_not include(p2) - assigns(:line_items).map(&:product).should_not include(p3) + assigns(:line_items).map(&:product).should include p1 + assigns(:line_items).map(&:product).should_not include p2, p3 end end describe 'Orders & Fulfillment' do - it "only shows product line items that I am supplying" do - spree_get :orders_and_fulfillment + context "where I have granted P-OC to the distributor" do + before do + create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:add_to_order_cycle]) + end - assigns(:line_items).map(&:product).should include(p1) - assigns(:line_items).map(&:product).should_not include(p2) - assigns(:line_items).map(&:product).should_not include(p3) + it "only shows product line items that I am supplying" do + spree_get :orders_and_fulfillment + + assigns(:line_items).map(&:product).should include p1 + assigns(:line_items).map(&:product).should_not include p2, p3 + end + end + + context "where I have not granted P-OC to the distributor" do + it "does not show me line_items I supply" do + spree_get :orders_and_fulfillment + + assigns(:line_items).map(&:product).should_not include p1, p2, p3 + end end it "only shows the selected order cycle" do @@ -143,7 +170,7 @@ describe Spree::Admin::ReportsController do it "should build distributors for the current user" do spree_get :products_and_inventory - assigns(:distributors).sort.should == [d1, d2, d3].sort + assigns(:distributors).sort.should == [c1, c2, d1, d2, d3].sort end it "builds suppliers for the current user" do @@ -184,7 +211,7 @@ describe Spree::Admin::ReportsController do it "should build distributors for the current user" do spree_get :customers - assigns(:distributors).sort.should == [d1, d2, d3].sort + assigns(:distributors).sort.should == [c1, c2, d1, d2, d3].sort end it "builds suppliers for the current user" do From f3f07662795f3b4aa281ad24bd0eaf9d3fc666aa Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 7 May 2015 12:48:31 +1000 Subject: [PATCH 062/151] Adding a distributor to order cycle to fix broken feature spec --- spec/features/admin/reports_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index b1685be6db..309e0a3e81 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -211,8 +211,9 @@ feature %q{ end it "handles order cycles with nil opening or closing times" do - oc = create(:simple_order_cycle, name: "My Order Cycle", orders_open_at: Time.now, orders_close_at: nil) - o = create(:order, order_cycle: oc) + distributor = create(:distributor_enterprise) + oc = create(:simple_order_cycle, name: "My Order Cycle", distributors: [distributor], orders_open_at: Time.now, orders_close_at: nil) + o = create(:order, order_cycle: oc, distributor: distributor) login_to_admin_section visit spree.orders_and_fulfillment_admin_reports_path From 6fb3fa55a15e1dcab42cecbf0bdcb02f771ccf2f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 7 May 2015 14:23:58 +1000 Subject: [PATCH 063/151] Allow extended time for all parts of this spec to fix intermittent fails --- spec/features/admin/authentication_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/features/admin/authentication_spec.rb b/spec/features/admin/authentication_spec.rb index 059963fd70..8473551c99 100644 --- a/spec/features/admin/authentication_spec.rb +++ b/spec/features/admin/authentication_spec.rb @@ -10,15 +10,15 @@ feature "Authentication", js: true do scenario "logging into admin redirects home, then back to admin" do # This is the first admin spec, so give a little extra load time for slow systems - Capybara.using_wait_time(60) do + Capybara.using_wait_time(120) do visit spree.admin_path - end - fill_in "Email", with: user.email - fill_in "Password", with: user.password - click_login_button - page.should have_content "DASHBOARD" - current_path.should == spree.admin_path + fill_in "Email", with: user.email + fill_in "Password", with: user.password + click_login_button + page.should have_content "DASHBOARD" + current_path.should == spree.admin_path + end end scenario "viewing my account" do From f7ade48e8655c282d765f06ed47dc865b142bbe0 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sat, 4 Apr 2015 17:06:26 +0100 Subject: [PATCH 064/151] Update DB schema to store updatable weight on items sold --- .../api/line_items_controller_decorator.rb | 17 +++++++++++++++++ .../20150305004846_add_weight_to_line_items.rb | 5 +++++ db/schema.rb | 1 + 3 files changed, 23 insertions(+) create mode 100644 app/controllers/spree/api/line_items_controller_decorator.rb create mode 100644 db/migrate/20150305004846_add_weight_to_line_items.rb diff --git a/app/controllers/spree/api/line_items_controller_decorator.rb b/app/controllers/spree/api/line_items_controller_decorator.rb new file mode 100644 index 0000000000..f9a025873d --- /dev/null +++ b/app/controllers/spree/api/line_items_controller_decorator.rb @@ -0,0 +1,17 @@ +Spree::Api::LineItemsController.class_eval do + after_filter :apply_enterprise_fees, :only => :update + + def apply_enterprise_fees + authorize! :read, order + order.update_distribution_charge! + end +end + + +#when we update a line item the .update_distribution_charge! is called +# order.should_receive .update_distribution_charge! +# check fails when absent + +# in order model check that .update_distribution_charge! is properly tested. +# think through use cases - existing completed order +# currently likely just used to complete orders so add test case that works on a completed order diff --git a/db/migrate/20150305004846_add_weight_to_line_items.rb b/db/migrate/20150305004846_add_weight_to_line_items.rb new file mode 100644 index 0000000000..a4df1a12f0 --- /dev/null +++ b/db/migrate/20150305004846_add_weight_to_line_items.rb @@ -0,0 +1,5 @@ +class AddWeightToLineItems < ActiveRecord::Migration + def change + add_column :spree_line_items, :unit_value, :decimal, :precision => 8, :scale => 2 + end +end diff --git a/db/schema.rb b/db/schema.rb index cb8b503583..96f5896c79 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -549,6 +549,7 @@ ActiveRecord::Schema.define(:version => 20150424025907) do t.string "currency" t.decimal "distribution_fee", :precision => 10, :scale => 2 t.string "shipping_method_name" + t.decimal "unit_value", :precision => 8, :scale => 2 end add_index "spree_line_items", ["order_id"], :name => "index_line_items_on_order_id" From 2f463474fba9f1ce9c2b01e1bb7940369482a8d9 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sat, 4 Apr 2015 17:44:02 +0100 Subject: [PATCH 065/151] Adding specs for variable weight adjustment via builk order management --- .../spree/api/line_items_controller_spec.rb | 31 +++++++++++++++++++ .../admin/bulk_order_management_spec.rb | 18 +++++++++-- .../unit/bulk_order_management_spec.js.coffee | 31 +++++++++++++++++-- 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 spec/controllers/spree/api/line_items_controller_spec.rb diff --git a/spec/controllers/spree/api/line_items_controller_spec.rb b/spec/controllers/spree/api/line_items_controller_spec.rb new file mode 100644 index 0000000000..e3c9a262af --- /dev/null +++ b/spec/controllers/spree/api/line_items_controller_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +module Spree + describe Spree::Api::LineItemsController do + render_views + + before do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => current_api_user + end + + def self.make_simple_data! + let!(:order) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now) } + let!(:line_item) { FactoryGirl.create(:line_item, order: order, unit_value: 500) } + end + + #test that when a line item is updated, an order's fees are updated too + context "as an admin user" do + sign_in_as_admin! + make_simple_data! + + context "as a line item is updated" do + it "apply enterprise fees on the item" do + line_item_params = { order_id: order.number, id: line_item.id, line_item: { id: line_item.id, unit_value: 520 }, format: :json} + controller.should_receive(:apply_enterprise_fees).and_return(true) + spree_post :update, line_item_params + end + end + end + end +end diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index c8ad230a00..7aa6449c1e 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -57,7 +57,7 @@ feature %q{ end it "displays a column for order date" do - page.should have_selector "th,date", text: "ORDER DATE", :visible => true + page.should have_selector "th.date", text: "ORDER DATE", :visible => true page.should have_selector "td.date", text: o1.completed_at.strftime("%F %T"), :visible => true page.should have_selector "td.date", text: o2.completed_at.strftime("%F %T"), :visible => true end @@ -141,8 +141,22 @@ feature %q{ admin_user = quick_login_as_admin end + let!(:p1) { FactoryGirl.create(:product_with_option_types, group_buy: true, group_buy_unit_size: 5000, variant_unit: "weight", variants: [FactoryGirl.create(:variant, unit_value: 1000)] ) } + let!(:v1) { p1.variants.first } let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 5 ) } + let!(:li1) { FactoryGirl.create(:line_item, order: o1, variant: v1, :quantity => 5, :unit_value => 1000 ) } + + context "modifying the weight/volume of a line item" do + it "update-pending is added to variable 'price'" do + visit '/admin/orders/bulk_management' + first("div#columns_dropdown", :text => "COLUMNS").click + first("div#columns_dropdown div.menu div.menu_item", text: "Weight/Volume").click + page.should_not have_css "input[name='price'].update-pending" + li1_unit_value_column = find("tr#li_#{li1.id} td.unit_value") + li1_unit_value_column.fill_in "unit_value", :with => 1200 + page.should have_css "input[name='price'].update-pending", :visible => false + end + end context "using column display toggle" do it "shows a column display toggle button, which shows a list of columns when clicked" do diff --git a/spec/javascripts/unit/bulk_order_management_spec.js.coffee b/spec/javascripts/unit/bulk_order_management_spec.js.coffee index 35b9e13d61..76ae925785 100644 --- a/spec/javascripts/unit/bulk_order_management_spec.js.coffee +++ b/spec/javascripts/unit/bulk_order_management_spec.js.coffee @@ -33,8 +33,8 @@ describe "AdminOrderMgmtCtrl", -> httpBackend.flush() expect(scope.suppliers).toEqual [{ id : '0', name : 'All' }, 'list of suppliers'] - expect(scope.distributors).toEqual [ { id : '0', name : 'All' }, 'list of distributors' ] - expect(scope.orderCycles).toEqual [ { id : '0', name : 'All' }, 'oc1', 'oc2', 'oc3' ] + expect(scope.distributors).toEqual [ { id : '0', name : 'All' }, 'list of distributors' ] + expect(scope.orderCycles).toEqual [ { id : '0', name : 'All' }, 'oc1', 'oc2', 'oc3' ] expect(scope.initialiseVariables.calls.length).toBe 1 expect(scope.fetchOrders.calls.length).toBe 1 @@ -350,6 +350,33 @@ describe "AdminOrderMgmtCtrl", -> spyOn(VariantUnitManager, "getUnitName").andReturn "kg" expect(scope.formattedValueWithUnitName(2000,unitsVariant)).toEqual "2 kg" + describe "updating the price upon updating the weight of a line item", -> + + it "resets the weight if the weight is set to zero", -> + scope.filteredLineItems = [ + { units_variant: { unit_value: 100 }, price: 2, unit_value: 0 } + ] + expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], 100)).toEqual scope.filteredLineItems[0].price + + it "updates the price if the weight is changed", -> + scope.filteredLineItems = [ + { units_variant: { unit_value: 100 }, price: 2, unit_value: 200 } + ] + old_value = scope.filteredLineItems[0].units_variant.unit_value + new_value = scope.filteredLineItems[0].unit_value + sp = scope.filteredLineItems[0].price * new_value / old_value + expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], old_value)).toEqual sp + + it "doesn't update the price if the weight is not changed", -> + scope.filteredLineItems = [ + { units_variant: { unit_value: 100 }, price: 2, unit_value: 100 } + ] + old_value = scope.filteredLineItems[0].unit_value + new_value = scope.filteredLineItems[0].unit_value + sp = scope.filteredLineItems[0].price + expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], old_value)).toEqual sp + + describe "managing pending changes", -> dataSubmitter = pendingChangesService = null From 2a991ad1300fda8dcf17fca2b9d6277f5d44c1ea Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sat, 4 Apr 2015 17:52:31 +0100 Subject: [PATCH 066/151] Variable Weights: Adding ability to update the weight/volume of a line_item after checkout. The price of the line_item is automatically updated to reflect the value of the new weight. --- .../admin/bulk_order_management.js.coffee | 9 +- .../directives/line_item_upd_attr.js.coffee | 6 +- app/models/spree/line_item_decorator.rb | 3 +- .../admin/orders/bulk_management.html.haml | 107 ++++++++++-------- .../spree/api/line_items/bulk_show.v1.rabl | 6 +- 5 files changed, 75 insertions(+), 56 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 4c1a319c1a..a20cdd27e7 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -32,7 +32,8 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ variant: { name: "Variant", visible: true } quantity: { name: "Quantity", visible: true } max: { name: "Max", visible: true } - + unit_value: { name: "Weight/Volume", visible: false } + price: { name: "Price", visible: false } $scope.initialise = -> $scope.initialiseVariables() authorise_api_reponse = "" @@ -162,6 +163,12 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.supplierFilter = $scope.suppliers[0].id $scope.orderCycleFilter = $scope.orderCycles[0].id $scope.quickSearch = "" + + $scope.weightAdjustedPrice = (lineItem, oldValue) -> + if lineItem.unit_value <= 0 + lineItem.unit_value = lineItem.units_variant.unit_value + lineItem.price = lineItem.price * lineItem.unit_value / oldValue + #$scope.bulk_order_form.line_item.price.$setViewValue($scope.bulk_order_form.line_item.price.$viewValue) ] daysFromToday = (days) -> diff --git a/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee b/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee index c83d7fdc0f..c5afce07a5 100644 --- a/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee +++ b/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee @@ -8,7 +8,9 @@ angular.module("ofn.admin").directive "ofnLineItemUpdAttr", [ scope.$watch -> scope.$eval(attrs.ngModel) , (value) -> - if ngModel.$dirty + #if ngModel.$dirty + # i think i can take this out, this directive is still only called + # on a change and only an updated value will create a db call. if value == element.dbValue pendingChanges.remove(scope.line_item.id, attrName) switchClass( element, "", ["update-pending", "update-error", "update-success"], false ) @@ -20,4 +22,4 @@ angular.module("ofn.admin").directive "ofnLineItemUpdAttr", [ url: "/api/orders/#{scope.line_item.order.number}/line_items/#{scope.line_item.id}?line_item[#{attrName}]=#{value}" pendingChanges.add(scope.line_item.id, attrName, changeObj) switchClass( element, "update-pending", ["update-error", "update-success"], false ) -] \ No newline at end of file +] diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index b2c0a583fc..4eec0bcd2b 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -1,5 +1,6 @@ Spree::LineItem.class_eval do - attr_accessible :max_quantity + attr_accessible :max_quantity, :unit_value + attr_accessible :unit_value, :price, :as => :api # -- Scopes scope :managed_by, lambda { |user| diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 61bf9fbb7e..a1f53826e1 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -100,53 +100,60 @@ %div{ :class => "sixteen columns alpha", 'ng-show' => '!loading && filteredLineItems.length == 0'} %h1#no_results No orders found. %div{ 'ng-hide' => 'loading || filteredLineItems.length == 0' } - %table.index#listing_orders.bulk{ :class => "sixteen columns alpha" } - %thead - %tr - %th.bulk - %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } - %th.order_no{ 'ng-show' => 'columns.order_no.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.number'; reverse = !reverse" } Order No. - %th.full_name{ 'ng-show' => 'columns.full_name.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.full_name'; reverse = !reverse" } Name - %th.email{ 'ng-show' => 'columns.email.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.email'; reverse = !reverse" } Email - %th.phone{ 'ng-show' => 'columns.phone.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } Phone - %th.date{ 'ng-show' => 'columns.order_date.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } Order Date - %th.producer{ 'ng-show' => 'columns.producer.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'supplier.name'; reverse = !reverse" } Producer - %th.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.order_cycle.name'; reverse = !reverse" } Order Cycle - %th.hub{ 'ng-show' => 'columns.hub.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } Hub - %th.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'units_variant.unit_text'; reverse = !reverse" } Product: Unit - %th.quantity{ 'ng-show' => 'columns.quantity.visible' } Quantity - %th.max{ 'ng-show' => 'columns.max.visible' } Max - %th.actions - %th.actions - Ask?  - %input{ :type => 'checkbox', 'ng-model' => "confirmDelete" } - %tr.line_item{ 'ng-repeat' => "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:predicate:reverse )", 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", :id => "li_{{line_item.id}}" } - %td.bulk - %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked' } - %td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }} - %td.full_name{ 'ng-show' => 'columns.full_name.visible' } {{ line_item.order.full_name }} - %td.email{ 'ng-show' => 'columns.email.visible' } {{ line_item.order.email }} - %td.phone{ 'ng-show' => 'columns.phone.visible' } {{ line_item.order.phone }} - %td.date{ 'ng-show' => 'columns.order_date.visible' } {{ line_item.order.completed_at }} - %td.producer{ 'ng-show' => 'columns.producer.visible' } {{ line_item.supplier.name }} - %td.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } {{ line_item.order.order_cycle.name }} - %td.hub{ 'ng-show' => 'columns.hub.visible' } {{ line_item.order.distributor.name }} - %td.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.unit_text }} - %td.quantity{ 'ng-show' => 'columns.quantity.visible' } - %input{ :type => 'number', :name => 'quantity', 'ng-model' => "line_item.quantity", 'ofn-line-item-upd-attr' => "quantity" } - %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} - %td.actions - %a{ :class => "edit-order icon-edit no-text", 'ofn-confirm-link-path' => "/admin/orders/{{line_item.order.number}}/edit" } - %td.actions - %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } - %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'pendingChanges.submitAll()' } \ No newline at end of file + %form{ 'ng-model' => "bulk_order_form" } + %table.index#listing_orders.bulk{ :class => "sixteen columns alpha" } + %thead + %tr + %th.bulk + %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } + %th.order_no{ 'ng-show' => 'columns.order_no.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.number'; reverse = !reverse" } Order No. + %th.full_name{ 'ng-show' => 'columns.full_name.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.full_name'; reverse = !reverse" } Name + %th.email{ 'ng-show' => 'columns.email.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.email'; reverse = !reverse" } Email + %th.phone{ 'ng-show' => 'columns.phone.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } Phone + %th.date{ 'ng-show' => 'columns.order_date.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } Order Date + %th.producer{ 'ng-show' => 'columns.producer.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'supplier.name'; reverse = !reverse" } Producer + %th.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.order_cycle.name'; reverse = !reverse" } Order Cycle + %th.hub{ 'ng-show' => 'columns.hub.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } Hub + %th.variant{ 'ng-show' => 'columns.variant.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'units_variant.unit_text'; reverse = !reverse" } Product: Unit + %th.quantity{ 'ng-show' => 'columns.quantity.visible' } Quantity + %th.max{ 'ng-show' => 'columns.max.visible' } Max + %th.unit_value{ 'ng-show' => 'columns.unit_value.visible' } Weight/Volume + %th.price{ 'ng-show' => 'columns.price.visible' } Price + %th.actions + %th.actions + Ask?  + %input{ :type => 'checkbox', 'ng-model' => "confirmDelete" } + %tr.line_item{ 'ng-repeat' => "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:predicate:reverse )", 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", :id => "li_{{line_item.id}}" } + %td.bulk + %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked' } + %td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }} + %td.full_name{ 'ng-show' => 'columns.full_name.visible' } {{ line_item.order.full_name }} + %td.email{ 'ng-show' => 'columns.email.visible' } {{ line_item.order.email }} + %td.phone{ 'ng-show' => 'columns.phone.visible' } {{ line_item.order.phone }} + %td.date{ 'ng-show' => 'columns.order_date.visible' } {{ line_item.order.completed_at }} + %td.producer{ 'ng-show' => 'columns.producer.visible' } {{ line_item.supplier.name }} + %td.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } {{ line_item.order.order_cycle.name }} + %td.hub{ 'ng-show' => 'columns.hub.visible' } {{ line_item.order.distributor.name }} + %td.variant{ 'ng-show' => 'columns.variant.visible' } + %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.unit_text }} + %td.quantity{ 'ng-show' => 'columns.quantity.visible' } + %input{ :type => 'number', :name => 'quantity', 'ng-model' => "line_item.quantity", 'ofn-line-item-upd-attr' => "quantity" } + %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} + %td.unit_value{ 'ng-show' => 'columns.unit_value.visible' } + %input{ :type => 'number', :name => 'unit_value', :id => 'unit_value', 'ng-model' => "line_item.unit_value", 'ng-change' => "weightAdjustedPrice(line_item, {{ line_item.unit_value }})", 'ofn-line-item-upd-attr' => "unit_value" } + %td.price{ 'ng-show' => 'columns.price.visible' } + %input{ :type => 'text', :name => 'price', :id => 'price', :value => '{{ line_item.price | currency }}', 'ng-model' => "line_item.price", 'ng-readonly' => "true", 'ofn-line-item-upd-attr' => "price" } + %td.actions + %a{ :class => "edit-order icon-edit no-text", 'ofn-confirm-link-path' => "/admin/orders/{{line_item.order.number}}/edit" } + %td.actions + %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } + %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'pendingChanges.submitAll()' } diff --git a/app/views/spree/api/line_items/bulk_show.v1.rabl b/app/views/spree/api/line_items/bulk_show.v1.rabl index 8b53c42086..6b24ef4bf2 100644 --- a/app/views/spree/api/line_items/bulk_show.v1.rabl +++ b/app/views/spree/api/line_items/bulk_show.v1.rabl @@ -1,5 +1,7 @@ object @line_item -attributes :id, :quantity, :max_quantity +attributes :id, :quantity, :max_quantity, :price node( :supplier ) { |li| partial 'api/enterprises/bulk_show', :object => li.product.supplier } node( :units_product ) { |li| partial 'spree/api/products/units_show', :object => li.product } -node( :units_variant ) { |li| partial 'spree/api/variants/units_show', :object => li.variant } \ No newline at end of file +node( :units_variant ) { |li| partial 'spree/api/variants/units_show', :object => li.variant } +node( :unit_value ) { |li| li.unit_value.to_f } +node( :price ) { |li| li.price } From 54da7ae241262a7e79666037828c02108fd0170e Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 14 Apr 2015 17:29:56 +0100 Subject: [PATCH 067/151] Adding additional logic for if the line_item unit_value is nil --- app/assets/javascripts/admin/bulk_order_management.js.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index a20cdd27e7..a52b770831 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -165,6 +165,8 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.quickSearch = "" $scope.weightAdjustedPrice = (lineItem, oldValue) -> + if oldValue <= 0 + oldValue = lineItem.units_variant.unit_value if lineItem.unit_value <= 0 lineItem.unit_value = lineItem.units_variant.unit_value lineItem.price = lineItem.price * lineItem.unit_value / oldValue From 662c7fe3681a9b454823607c8654213a147af251 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Fri, 24 Apr 2015 16:14:24 +0100 Subject: [PATCH 068/151] Removing notes to myself from this file --- .../spree/api/line_items_controller_decorator.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/controllers/spree/api/line_items_controller_decorator.rb b/app/controllers/spree/api/line_items_controller_decorator.rb index f9a025873d..35fca864f4 100644 --- a/app/controllers/spree/api/line_items_controller_decorator.rb +++ b/app/controllers/spree/api/line_items_controller_decorator.rb @@ -6,12 +6,3 @@ Spree::Api::LineItemsController.class_eval do order.update_distribution_charge! end end - - -#when we update a line item the .update_distribution_charge! is called -# order.should_receive .update_distribution_charge! -# check fails when absent - -# in order model check that .update_distribution_charge! is properly tested. -# think through use cases - existing completed order -# currently likely just used to complete orders so add test case that works on a completed order From 521834bd76d8a22b6279c49262821fa97528abec Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Fri, 24 Apr 2015 16:17:00 +0100 Subject: [PATCH 069/151] Populate the line item unit value, when line_item created and update old data in migration --- app/models/spree/order_decorator.rb | 1 + .../20150424151117_populate_line_item_unit_value.rb | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 db/migrate/20150424151117_populate_line_item_unit_value.rb diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 0dc6977965..cbc5c0086d 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -127,6 +127,7 @@ Spree::Order.class_eval do else current_item = Spree::LineItem.new(:quantity => quantity, max_quantity: max_quantity) current_item.variant = variant + current_item.unit_value = variant.unit_value if currency current_item.currency = currency unless currency.nil? current_item.price = variant.price_in(currency).amount diff --git a/db/migrate/20150424151117_populate_line_item_unit_value.rb b/db/migrate/20150424151117_populate_line_item_unit_value.rb new file mode 100644 index 0000000000..2122b84472 --- /dev/null +++ b/db/migrate/20150424151117_populate_line_item_unit_value.rb @@ -0,0 +1,9 @@ +class PopulateLineItemUnitValue < ActiveRecord::Migration + def up + execute "UPDATE spree_line_items SET unit_value = spree_variants.unit_value FROM spree_variants WHERE spree_line_items.variant_id = spree_variants.id" + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end From 4d025ee7a923060a8e09d5977f8136094132b726 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Fri, 24 Apr 2015 16:22:17 +0100 Subject: [PATCH 070/151] Updating the spec based on @Robs suggestions, hoping for his insights. Still doesn't work. --- spec/controllers/spree/api/line_items_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/spree/api/line_items_controller_spec.rb b/spec/controllers/spree/api/line_items_controller_spec.rb index e3c9a262af..7d0b215ae9 100644 --- a/spec/controllers/spree/api/line_items_controller_spec.rb +++ b/spec/controllers/spree/api/line_items_controller_spec.rb @@ -20,9 +20,9 @@ module Spree make_simple_data! context "as a line item is updated" do - it "apply enterprise fees on the item" do + it "update distribution charge on the order" do line_item_params = { order_id: order.number, id: line_item.id, line_item: { id: line_item.id, unit_value: 520 }, format: :json} - controller.should_receive(:apply_enterprise_fees).and_return(true) + order.should_receive(:update_distribution_charge!).and_call_original spree_post :update, line_item_params end end From 3179887842309f497ffb8685cca3ac726d64dcf9 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Sat, 9 May 2015 19:03:06 +0100 Subject: [PATCH 071/151] Do not allow line_item.unit_value to be updated if the variant.unit_value is zero --- .../javascripts/admin/bulk_order_management.js.coffee | 6 ++++++ app/views/spree/admin/orders/bulk_management.html.haml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index a52b770831..bf80b9f353 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -171,6 +171,12 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ lineItem.unit_value = lineItem.units_variant.unit_value lineItem.price = lineItem.price * lineItem.unit_value / oldValue #$scope.bulk_order_form.line_item.price.$setViewValue($scope.bulk_order_form.line_item.price.$viewValue) + + $scope.unitValueLessThanZero = (lineItem) -> + if lineItem.units_variant.unit_value <= 0 + true + else + false ] daysFromToday = (days) -> diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index a1f53826e1..f6680aefae 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -149,7 +149,7 @@ %input{ :type => 'number', :name => 'quantity', 'ng-model' => "line_item.quantity", 'ofn-line-item-upd-attr' => "quantity" } %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} %td.unit_value{ 'ng-show' => 'columns.unit_value.visible' } - %input{ :type => 'number', :name => 'unit_value', :id => 'unit_value', 'ng-model' => "line_item.unit_value", 'ng-change' => "weightAdjustedPrice(line_item, {{ line_item.unit_value }})", 'ofn-line-item-upd-attr' => "unit_value" } + %input{ :type => 'number', :name => 'unit_value', :id => 'unit_value', 'ng-model' => "line_item.unit_value", 'ng-readonly' => "unitValueLessThanZero(line_item)", 'ng-change' => "weightAdjustedPrice(line_item, {{ line_item.unit_value }})", 'ofn-line-item-upd-attr' => "unit_value" } %td.price{ 'ng-show' => 'columns.price.visible' } %input{ :type => 'text', :name => 'price', :id => 'price', :value => '{{ line_item.price | currency }}', 'ng-model' => "line_item.price", 'ng-readonly' => "true", 'ofn-line-item-upd-attr' => "price" } %td.actions From 62ae38372ef4f47ae1b03881a7375e0dfffc1b87 Mon Sep 17 00:00:00 2001 From: Rick Giner Date: Mon, 11 May 2015 20:31:24 +1000 Subject: [PATCH 072/151] #541 add "show more" link to producers lists in hub on home page --- .../controllers/hub_node_controller.js.coffee | 4 ++-- .../stylesheets/darkswarm/hub_node.css.sass | 17 +++++++++++++++++ app/views/home/_fat.html.haml | 18 +++++++++++++++--- app/views/home/_skinny.html.haml | 2 +- app/views/producers/_fat.html.haml | 2 +- app/views/producers/_skinny.html.haml | 2 +- 6 files changed, 37 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/hub_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/hub_node_controller.js.coffee index e73ca5fa27..922dc8abb4 100644 --- a/app/assets/javascripts/darkswarm/controllers/hub_node_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/hub_node_controller.js.coffee @@ -1,6 +1,6 @@ Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, Navigation, $location, $templateCache, CurrentHub) -> - $scope.toggle = -> - HashNavigation.toggle $scope.hub.hash + $scope.toggle = (e) -> + HashNavigation.toggle $scope.hub.hash if !angular.element(e.target).inheritedData('is-link') $scope.open = -> HashNavigation.active $scope.hub.hash diff --git a/app/assets/stylesheets/darkswarm/hub_node.css.sass b/app/assets/stylesheets/darkswarm/hub_node.css.sass index d02f9b5299..dfe4d42074 100644 --- a/app/assets/stylesheets/darkswarm/hub_node.css.sass +++ b/app/assets/stylesheets/darkswarm/hub_node.css.sass @@ -83,6 +83,23 @@ .active_table_row:first-child .skinny-head background-color: rgba(255,255,255,0.85) + .producers-list + li.more-producers-link + .less + display: none + a:hover + text-decoration: underline + li.additional-producer + display: none + &.show-more-producers + li.additional-producer + display: block + li.more-producers-link + .more + display: none + .less + display: block + //INACTIVE - closed hub &.inactive &.closed, &.open diff --git a/app/views/home/_fat.html.haml b/app/views/home/_fat.html.haml index 6796601637..092d9e898d 100644 --- a/app/views/home/_fat.html.haml +++ b/app/views/home/_fat.html.haml @@ -1,4 +1,4 @@ -.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle()", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} +.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} .columns.small-12.medium-6.large-5.fat %div{"bo-if" => "hub.taxons"} %label Shop for @@ -21,8 +21,20 @@ .columns.small-12.medium-3.large-5.fat %div{"bo-if" => "hub.producers"} %label Our producers - %ul.small-block-grid-2.medium-block-grid-1.large-block-grid-2 - %li{"ng-repeat" => "enterprise in hub.producers"} + %ul.small-block-grid-2.medium-block-grid-1.large-block-grid-2{"ng-class" => "{'show-more-producers' : toggleMoreProducers}", "class" => "producers-list"} + %li{"ng-repeat" => "enterprise in hub.producers | limitTo:7"} + %enterprise-modal + %i.ofn-i_036-producers + %span{"bo-text" => "enterprise.name"} + %li{"data-is-link" => "true", "class" => "more-producers-link"} + %a{"ng-click" => "toggleMoreProducers=!toggleMoreProducers"} + .more + + + %span{"bo-text" => "hub.producers.length-7"} + More + .less + Show less + %li{"ng-repeat" => "enterprise in hub.producers.slice(7,hub.producers.length)", "class" => "additional-producer"} %enterprise-modal %i.ofn-i_036-producers %span{"bo-text" => "enterprise.name"} diff --git a/app/views/home/_skinny.html.haml b/app/views/home/_skinny.html.haml index cf001371f7..9cf7a01c77 100644 --- a/app/views/home/_skinny.html.haml +++ b/app/views/home/_skinny.html.haml @@ -1,4 +1,4 @@ -.row.active_table_row{"ng-if" => "hub.is_distributor", "ng-click" => "toggle()", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}", bindonce: true} +.row.active_table_row{"ng-if" => "hub.is_distributor", "ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}", bindonce: true} .columns.small-12.medium-6.large-5.skinny-head %a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml index a6d6661501..99099a31f8 100644 --- a/app/views/producers/_fat.html.haml +++ b/app/views/producers/_fat.html.haml @@ -1,4 +1,4 @@ -.row.active_table_row{"ng-if" => "open()", "ng-click" => "toggle()", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} +.row.active_table_row{"ng-if" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} .columns.small-12.medium-7.large-7.fat / Will add in long description available once clean up HTML formatting producer.long_description diff --git a/app/views/producers/_skinny.html.haml b/app/views/producers/_skinny.html.haml index 49ab0a35be..11860d35d6 100644 --- a/app/views/producers/_skinny.html.haml +++ b/app/views/producers/_skinny.html.haml @@ -1,4 +1,4 @@ -.row.active_table_row{"ng-click" => "toggle()", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}"} +.row.active_table_row{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}"} .columns.small-12.medium-4.large-4.skinny-head %span{"bo-if" => "producer.is_distributor" } %a.is_distributor{"bo-href" => "producer.path" } From 3520127c41a7928e25ba1ea3d1e7dfd86339ff4e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 13 May 2015 11:06:42 +1000 Subject: [PATCH 073/151] Fix infinite job loop --- app/models/spree/user_decorator.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 8298627547..d8ea312e23 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -1,5 +1,9 @@ Spree.user_class.class_eval do - handle_asynchronously :send_reset_password_instructions + if method_defined? :send_reset_password_instructions_with_delay + Bugsnag.notify RuntimeError.new "send_reset_password_instructions already handled asyncronously - double-calling results in infinite job loop" + else + handle_asynchronously :send_reset_password_instructions + end has_many :enterprise_roles, :dependent => :destroy has_many :enterprises, through: :enterprise_roles From b86872095a4425fe5047a4a800b0ecf868e04382 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 13 May 2015 14:52:17 +1000 Subject: [PATCH 074/151] Add google analytics --- app/views/layouts/darkswarm.html.haml | 1 + app/views/shared/_analytics.html.haml | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 app/views/shared/_analytics.html.haml diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index e1976ab167..a627ba4896 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -41,3 +41,4 @@ #footer %loading + = render 'shared/analytics' diff --git a/app/views/shared/_analytics.html.haml b/app/views/shared/_analytics.html.haml new file mode 100644 index 0000000000..ee9ba69923 --- /dev/null +++ b/app/views/shared/_analytics.html.haml @@ -0,0 +1,8 @@ +:javascript + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-62912229-1', 'auto'); + ga('send', 'pageview'); From a473d0ed11cdce163d37fac574b31df35fa4b6d5 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 13 May 2015 15:58:03 +1000 Subject: [PATCH 075/151] Checking in db version change --- db/schema.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 96f5896c79..f9f32af7d1 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 => 20150424025907) do +ActiveRecord::Schema.define(:version => 20150424151117) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -619,9 +619,9 @@ ActiveRecord::Schema.define(:version => 20150424025907) 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" end From ef064819f9943a81066f0785edb8d2ef8ef096f9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 10:41:29 +1000 Subject: [PATCH 076/151] Add spec for order_cycle_management report access --- spec/models/spree/ability_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 0dc9364967..9ed432e4af 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -213,7 +213,7 @@ module Spree end it "should be able to read some reports" do - should have_ability([:admin, :index, :customers, :bulk_coop, :orders_and_fulfillment, :products_and_inventory], for: :report) + should have_ability([:admin, :index, :customers, :bulk_coop, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], for: :report) end it "should not be able to read other reports" do @@ -400,7 +400,7 @@ module Spree end it "should be able to read some reports" do - should have_ability([:admin, :index, :customers, :sales_tax, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory], for: :report) + should have_ability([:admin, :index, :customers, :sales_tax, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], for: :report) end it "should not be able to read other reports" do From 312a6299a84d85dc1b5b87291ea3dc7e3edabdb3 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 15 May 2015 21:19:16 +1000 Subject: [PATCH 077/151] Making where clause unambiguous --- app/controllers/spree/admin/reports_controller_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 3aa4f560a1..34f2d640c2 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -416,7 +416,7 @@ Spree::Admin::ReportsController.class_eval do @line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders)) @line_items = @line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present? - line_items_with_hidden_details = @line_items.where("id NOT IN (?)", permissions.editable_line_items) + line_items_with_hidden_details = @line_items.where('"spree_line_items"."id" NOT IN (?)', permissions.editable_line_items) @line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item| # TODO We should really be hiding customer code here too, but until we # have an actual association between order and customer, it's a bit tricky From 6b956a8a38c904347fd95a482b80f12eed3a1377 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 May 2015 10:19:37 +1000 Subject: [PATCH 078/151] Updating product clone spec --- .../unit/admin/services/bulk_products_spec.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee b/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee index 0b3c0841a6..54b57aed48 100644 --- a/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee @@ -68,7 +68,7 @@ describe "BulkProducts service", -> $httpBackend.expectGET("/api/products/17?template=bulk_show").respond 200, clonedProduct BulkProducts.cloneProduct BulkProducts.products[0] $httpBackend.flush() - expect(BulkProducts.addProducts).toHaveBeenCalledWith [clonedProduct] + expect(BulkProducts.insertProductAfter).toHaveBeenCalledWith originalProduct, clonedProduct describe "preparing products", -> From 828456118b23429d9c48696fb88e4fdc08d5f697 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 May 2015 11:15:18 +1000 Subject: [PATCH 079/151] Remove forgotten binding.pry --- app/models/spree/ability_decorator.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 7bc6fb6535..e652825911 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -154,7 +154,6 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization can [:destroy], Spree::Adjustment do |adjustment| # Sharing code with destroying a line item. This should be unified and probably applied for other actions as well. - binding.pry if user.admin? true elsif adjustment.adjustable.instance_of? Spree::Order From 7f80c02c0ea0736cca16117823dc48c55b9e4240 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 11:45:05 +1000 Subject: [PATCH 080/151] Adding route for managed route for admin orders --- config/routes.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index 4621ee4a35..f8b70ba4d6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -169,6 +169,10 @@ Spree::Core::Engine.routes.prepend do post :bulk_update, :on => :collection, :as => :bulk_update end + + resources :orders do + get :managed, on: :collection + end end resources :orders do From 823adf32721a1cd0d44ea18497f79b5d43b03f5b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 11:45:48 +1000 Subject: [PATCH 081/151] Translating existing order-related rabl templates accross to AMS --- .../api/admin/basic_order_cycle_serializer.rb | 14 +++++++++ .../api/admin/line_item_serializer.rb | 15 +++++++++ app/serializers/api/admin/order_serializer.rb | 31 +++++++++++++++++++ .../api/admin/units_product_serializer.rb | 3 ++ .../api/admin/units_variant_serializer.rb | 8 +++++ 5 files changed, 71 insertions(+) create mode 100644 app/serializers/api/admin/basic_order_cycle_serializer.rb create mode 100644 app/serializers/api/admin/line_item_serializer.rb create mode 100644 app/serializers/api/admin/order_serializer.rb create mode 100644 app/serializers/api/admin/units_product_serializer.rb create mode 100644 app/serializers/api/admin/units_variant_serializer.rb diff --git a/app/serializers/api/admin/basic_order_cycle_serializer.rb b/app/serializers/api/admin/basic_order_cycle_serializer.rb new file mode 100644 index 0000000000..e94795821a --- /dev/null +++ b/app/serializers/api/admin/basic_order_cycle_serializer.rb @@ -0,0 +1,14 @@ +class Api::Admin::BasicOrderCycleSerializer < ActiveModel::Serializer + attributes :id, :name, :first_order, :last_order + + has_many :suppliers, serializer: Api::Admin::IdNameSerializer + has_many :distributors, serializer: Api::Admin::IdNameSerializer + + def first_order + object.orders_open_at.strftime("%F") + end + + def last_order + (object.orders_close_at + 1.day).strftime("%F") + end +end diff --git a/app/serializers/api/admin/line_item_serializer.rb b/app/serializers/api/admin/line_item_serializer.rb new file mode 100644 index 0000000000..784e1e8be3 --- /dev/null +++ b/app/serializers/api/admin/line_item_serializer.rb @@ -0,0 +1,15 @@ +class Api::Admin::LineItemSerializer < ActiveModel::Serializer + attributes :id, :quantity, :max_quantity, :supplier, :units_product, :units_variant + + def supplier + Api::Admin::IdNameSerializer.new(object.product.supplier).serializable_hash + end + + def units_product + Api::Admin::UnitsProductSerializer.new(object.product).serializable_hash + end + + def units_variant + Api::Admin::UnitsVariantSerializer.new(object.variant).serializable_hash + end +end diff --git a/app/serializers/api/admin/order_serializer.rb b/app/serializers/api/admin/order_serializer.rb new file mode 100644 index 0000000000..2277551989 --- /dev/null +++ b/app/serializers/api/admin/order_serializer.rb @@ -0,0 +1,31 @@ +class Api::Admin::OrderSerializer < ActiveModel::Serializer + attributes :id, :number, :full_name, :email, :phone, :completed_at, :line_items + + has_one :distributor, serializer: Api::Admin::IdNameSerializer + has_one :order_cycle, serializer: Api::Admin::BasicOrderCycleSerializer + + def full_name + object.billing_address.nil? ? "" : ( object.billing_address.full_name || "" ) + end + + def email + object.email || "" + end + + def phone + object.billing_address.nil? ? "a" : ( object.billing_address.phone || "" ) + end + + def completed_at + object.completed_at.blank? ? "" : object.completed_at.strftime("%F %T") + end + + def line_items + # we used to have a scope here, but we are at the point where a user which can edit an order + # should be able to edit all of the line_items as well, making the scope redundant + ActiveModel::ArraySerializer.new( + object.line_items.order('id ASC'), + {each_serializer: Api::Admin::LineItemSerializer} + ) + end +end diff --git a/app/serializers/api/admin/units_product_serializer.rb b/app/serializers/api/admin/units_product_serializer.rb new file mode 100644 index 0000000000..37521b2bb9 --- /dev/null +++ b/app/serializers/api/admin/units_product_serializer.rb @@ -0,0 +1,3 @@ +class Api::Admin::UnitsProductSerializer < ActiveModel::Serializer + attributes :id, :name, :group_buy_unit_size, :variant_unit +end diff --git a/app/serializers/api/admin/units_variant_serializer.rb b/app/serializers/api/admin/units_variant_serializer.rb new file mode 100644 index 0000000000..e96f9299f6 --- /dev/null +++ b/app/serializers/api/admin/units_variant_serializer.rb @@ -0,0 +1,8 @@ +class Api::Admin::UnitsVariantSerializer < ActiveModel::Serializer + attributes :id, :unit_text, :unit_value + + def unit_text + options_text = object.options_text + object.product.name + (options_text.empty? ? "" : ": #{options_text}") + end +end From fc55a000b87e73cee57129cbf689edccbd620b5b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 11:48:13 +1000 Subject: [PATCH 082/151] Adding managed controller action which uses new order serializer to render json --- .../admin/orders_controller_decorator.rb | 10 +- app/models/spree/ability_decorator.rb | 3 +- .../spree/admin/orders_controller_spec.rb | 139 +++++++++++++++++- 3 files changed, 147 insertions(+), 5 deletions(-) diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index 42e7068afc..247e780deb 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -7,7 +7,7 @@ Spree::Admin::OrdersController.class_eval do # We need to add expections for collection actions other than :index here # because spree_auth_devise causes load_order to be called, which results # in an auth failure as the @order object is nil for collection actions - before_filter :check_authorization, :except => :bulk_management + before_filter :check_authorization, except: [:bulk_management, :managed] # After updating an order, the fees should be updated as well # Currently, adding or deleting line items does not trigger updating the @@ -17,7 +17,7 @@ Spree::Admin::OrdersController.class_eval do after_filter :update_distribution_charge, :only => :update respond_override :index => { :html => - { :success => lambda { + { :success => lambda { # Filter orders to only show those distributed by current user (or all for admin user) @orders = @search.result.includes([:user, :shipments, :payments]). distributed_by_user(spree_current_user). @@ -37,4 +37,10 @@ Spree::Admin::OrdersController.class_eval do def update_distribution_charge @order.update_distribution_charge! end + + def managed + permissions = OpenFoodNetwork::Permissions.new(spree_current_user) + @orders = permissions.editable_orders.ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) + render json: @orders, each_serializer: Api::Admin::OrderSerializer + end end diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 7bc6fb6535..13fe55a852 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -142,7 +142,7 @@ class AbilityDecorator # during the order creation process from the admin backend order.distributor.nil? || user.enterprises.include?(order.distributor) end - can [:admin, :bulk_management], Spree::Order if user.admin? || user.enterprises.any?(&:is_distributor) + can [:admin, :bulk_management, :managed], Spree::Order if user.admin? || user.enterprises.any?(&:is_distributor) can [:admin, :create], Spree::LineItem can [:destroy], Spree::LineItem do |item| user.admin? || user.enterprises.include?(order.distributor) || user == order.order_cycle.manager @@ -154,7 +154,6 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization can [:destroy], Spree::Adjustment do |adjustment| # Sharing code with destroying a line item. This should be unified and probably applied for other actions as well. - binding.pry if user.admin? true elsif adjustment.adjustable.instance_of? Spree::Order diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index 5724732c50..ac6a5532b2 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -1,9 +1,10 @@ require 'spec_helper' describe Spree::Admin::OrdersController do - let!(:order) { create(:order) } + include AuthenticationWorkflow context "updating an order with line items" do + let!(:order) { create(:order) } let(:line_item) { create(:line_item) } before { login_as_admin } @@ -27,4 +28,140 @@ describe Spree::Admin::OrdersController do } end end + + describe "managed" do + render_views + + let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :line_items, :distributor, :order_cycle, :number] } + + def self.make_simple_data! + let!(:dist1) { FactoryGirl.create(:distributor_enterprise) } + let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:order3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:line_item1) { FactoryGirl.create(:line_item, order: order1) } + let!(:line_item2) { FactoryGirl.create(:line_item, order: order2) } + let!(:line_item3) { FactoryGirl.create(:line_item, order: order2) } + let!(:line_item4) { FactoryGirl.create(:line_item, order: order3) } + let(:line_item_attributes) { [:id, :quantity, :max_quantity, :supplier, :units_product, :units_variant] } + end + + context "as a normal user" do + before { controller.stub spree_current_user: create_enterprise_user } + + make_simple_data! + + it "should deny me access to managed orders" do + spree_get :managed, { :template => 'bulk_index', :format => :json } + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as an administrator" do + make_simple_data! + + before do + controller.stub spree_current_user: quick_login_as_admin + spree_get :managed, { :template => 'bulk_index', :format => :json } + end + + it "retrieves a list of orders with appropriate attributes, including line items with appropriate attributes" do + keys = json_response.first.keys.map{ |key| key.to_sym } + order_attributes.all?{ |attr| keys.include? attr }.should == true + end + + it "retrieves a list of line items with appropriate attributes" do + li_keys = json_response.first['line_items'].first.keys.map{ |key| key.to_sym } + line_item_attributes.all?{ |attr| li_keys.include? attr }.should == true + end + + it "sorts orders in ascending id order" do + ids = json_response.map{ |order| order['id'] } + ids[0].should < ids[1] + ids[1].should < ids[2] + end + + it "formats completed_at to 'yyyy-mm-dd hh:mm'" do + json_response.map{ |order| order['completed_at'] }.all?{ |a| a.match("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$") }.should == true + end + + it "returns an array for line_items" do + json_response.map{ |order| order['line_items'] }.all?{ |a| a.is_a? Array }.should == true + end + + it "returns quantity and max quantity at integers" do + json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['quantity'] }.all?{ |q| q.is_a? Fixnum }.should == true + json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['max_quantity'] }.all?{ |mq| mq.nil? || mq.is_a?( Fixnum ) }.should == true + end + + it "returns supplier object with id and name keys" do + json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['supplier'] }.all?{ |s| s.has_key?('id') && s.has_key?('name') }.should == true + end + + it "returns distributor object with id and name keys" do + json_response.map{ |order| order['distributor'] }.all?{ |d| d.has_key?('id') && d.has_key?('name') }.should == true + end + + it "retrieves the order number" do + json_response.map{ |order| order['number'] }.all?{ |number| number.match("^R\\d{5,10}$") }.should == true + end + end + + context "as an enterprise user" do + let(:supplier) { create(:supplier_enterprise) } + let(:distributor1) { create(:distributor_enterprise) } + let(:distributor2) { create(:distributor_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } + let!(:order1) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.now, distributor: distributor1, billing_address: FactoryGirl.create(:address) ) } + let!(:line_item1) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } + let!(:line_item2) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } + let!(:order2) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.now, distributor: distributor2, billing_address: FactoryGirl.create(:address) ) } + let!(:line_item3) { FactoryGirl.create(:line_item, order: order2, product: FactoryGirl.create(:product, supplier: supplier)) } + + context "producer enterprise" do + + before do + controller.stub spree_current_user: supplier.owner + spree_get :managed, { :format => :json } + end + + it "does not display line items for which my enterprise is a supplier" do + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "coordinator enterprise" do + before do + controller.stub spree_current_user: coordinator.owner + spree_get :managed, { :format => :json } + end + + it "retrieves a list of orders" do + keys = json_response.first.keys.map{ |key| key.to_sym } + order_attributes.all?{ |attr| keys.include? attr }.should == true + end + + it "only displays line items from orders for which my enterprise is the order_cycle coorinator" do + json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.sort.should == [line_item1.id, line_item2.id, line_item3.id].sort + end + end + + context "hub enterprise" do + before do + controller.stub spree_current_user: distributor1.owner + spree_get :managed, { :format => :json } + end + + it "retrieves a list of orders" do + keys = json_response.first.keys.map{ |key| key.to_sym } + order_attributes.all?{ |attr| keys.include? attr }.should == true + end + + it "only displays line items from orders for which my enterprise is a distributor" do + json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.sort.should == [line_item1.id, line_item2.id].sort + end + end + end + end end From 0ad2978926f17e663b96bafffe4f8365e660af50 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 11:48:48 +1000 Subject: [PATCH 083/151] Removing old managed route from api orders controller and switching BOM over to use new controller action --- .../admin/bulk_order_management.js.coffee | 2 +- .../spree/api/orders_controller_decorator.rb | 8 -- .../spree/api/orders_controller_spec.rb | 121 ------------------ .../unit/bulk_order_management_spec.js.coffee | 2 +- 4 files changed, 2 insertions(+), 131 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 20ddc177ba..076dd55e3a 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -62,7 +62,7 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.fetchOrders = -> $scope.loading = true - dataFetcher("/api/orders/managed?template=bulk_index;page=1;per_page=500;q[state_not_eq]=canceled;q[completed_at_not_null]=true;q[completed_at_gt]=#{$scope.startDate};q[completed_at_lt]=#{$scope.endDate}").then (data) -> + dataFetcher("/admin/orders/managed?template=bulk_index;page=1;per_page=500;q[state_not_eq]=canceled;q[completed_at_not_null]=true;q[completed_at_gt]=#{$scope.startDate};q[completed_at_lt]=#{$scope.endDate}").then (data) -> $scope.resetOrders data $scope.loading = false diff --git a/app/controllers/spree/api/orders_controller_decorator.rb b/app/controllers/spree/api/orders_controller_decorator.rb index ca1fc4a570..21f64f51d8 100644 --- a/app/controllers/spree/api/orders_controller_decorator.rb +++ b/app/controllers/spree/api/orders_controller_decorator.rb @@ -4,12 +4,4 @@ Spree::Api::OrdersController.class_eval do # because Spree's API controller causes authorize_read! to be called, which # results in an ActiveRecord::NotFound Exception as the order object is not # defined for collection actions - before_filter :authorize_read!, :except => [:managed] - - def managed - authorize! :admin, Spree::Order - authorize! :read, Spree::Order - @orders = Spree::Order.ransack(params[:q]).result.distributed_by_user(current_api_user).page(params[:page]).per(params[:per_page]) - respond_with(@orders, default_template: :index) - end end diff --git a/spec/controllers/spree/api/orders_controller_spec.rb b/spec/controllers/spree/api/orders_controller_spec.rb index ec87169f76..5b651016f3 100644 --- a/spec/controllers/spree/api/orders_controller_spec.rb +++ b/spec/controllers/spree/api/orders_controller_spec.rb @@ -3,127 +3,6 @@ require 'spree/api/testing_support/helpers' module Spree describe Spree::Api::OrdersController do - include Spree::Api::TestingSupport::Helpers - render_views - before do - stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => current_api_user - end - - let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :line_items, :distributor, :order_cycle, :number] } - - def self.make_simple_data! - let!(:dist1) { FactoryGirl.create(:distributor_enterprise) } - let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } - let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } - let!(:order3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } - let!(:line_item1) { FactoryGirl.create(:line_item, order: order1) } - let!(:line_item2) { FactoryGirl.create(:line_item, order: order2) } - let!(:line_item3) { FactoryGirl.create(:line_item, order: order2) } - let!(:line_item4) { FactoryGirl.create(:line_item, order: order3) } - let(:line_item_attributes) { [:id, :quantity, :max_quantity, :supplier, :units_product, :units_variant] } - end - - - context "as a normal user" do - sign_in_as_user! - make_simple_data! - - it "should deny me access to managed orders" do - spree_get :managed, { :template => 'bulk_index', :format => :json } - assert_unauthorized! - end - end - - context "as an administrator" do - sign_in_as_admin! - make_simple_data! - - before :each do - spree_get :managed, { :template => 'bulk_index', :format => :json } - end - - it "retrieves a list of orders with appropriate attributes, including line items with appropriate attributes" do - keys = json_response.first.keys.map{ |key| key.to_sym } - order_attributes.all?{ |attr| keys.include? attr }.should == true - end - - it "retrieves a list of line items with appropriate attributes" do - li_keys = json_response.first['line_items'].first.keys.map{ |key| key.to_sym } - line_item_attributes.all?{ |attr| li_keys.include? attr }.should == true - end - - it "sorts orders in ascending id order" do - ids = json_response.map{ |order| order['id'] } - ids[0].should < ids[1] - ids[1].should < ids[2] - end - - it "formats completed_at to 'yyyy-mm-dd hh:mm'" do - json_response.map{ |order| order['completed_at'] }.all?{ |a| a.match("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$") }.should == true - end - - it "returns an array for line_items" do - json_response.map{ |order| order['line_items'] }.all?{ |a| a.is_a? Array }.should == true - end - - it "returns quantity and max quantity at integers" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['quantity'] }.all?{ |q| q.is_a? Fixnum }.should == true - json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['max_quantity'] }.all?{ |mq| mq.nil? || mq.is_a?( Fixnum ) }.should == true - end - - it "returns supplier object with id and name keys" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['supplier'] }.all?{ |s| s.has_key?('id') && s.has_key?('name') }.should == true - end - - it "returns distributor object with id and name keys" do - json_response.map{ |order| order['distributor'] }.all?{ |d| d.has_key?('id') && d.has_key?('name') }.should == true - end - - it "retrieves the order number" do - json_response.map{ |order| order['number'] }.all?{ |number| number.match("^R\\d{5,10}$") }.should == true - end - end - - context "as an enterprise user" do - let(:supplier) { create(:supplier_enterprise) } - let(:distributor1) { create(:distributor_enterprise) } - let(:distributor2) { create(:distributor_enterprise) } - let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: distributor1, billing_address: FactoryGirl.create(:address) ) } - let!(:line_item1) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } - let!(:line_item2) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } - let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: distributor2, billing_address: FactoryGirl.create(:address) ) } - let!(:line_item3) { FactoryGirl.create(:line_item, order: order2, product: FactoryGirl.create(:product, supplier: supplier)) } - - context "producer enterprise" do - sign_in_as_enterprise_user! [:supplier] - - before :each do - spree_get :managed, { :template => 'bulk_index', :format => :json } - end - - it "does not display line item for which my enteprise is a supplier" do - response.status.should == 401 - end - end - - context "hub enterprise" do - sign_in_as_enterprise_user! [:distributor1] - - before :each do - spree_get :managed, { :template => 'bulk_index', :format => :json } - end - - it "retrieves a list of orders" do - keys = json_response.first.keys.map{ |key| key.to_sym } - order_attributes.all?{ |attr| keys.include? attr }.should == true - end - - it "only displays line items from orders for which my enterprise is a distributor" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.should == [line_item1.id, line_item2.id] - end - end - end end end diff --git a/spec/javascripts/unit/bulk_order_management_spec.js.coffee b/spec/javascripts/unit/bulk_order_management_spec.js.coffee index 3377c799db..71ebb9f3b9 100644 --- a/spec/javascripts/unit/bulk_order_management_spec.js.coffee +++ b/spec/javascripts/unit/bulk_order_management_spec.js.coffee @@ -43,7 +43,7 @@ describe "AdminOrderMgmtCtrl", -> describe "fetching orders", -> beforeEach -> scope.initialiseVariables() - httpBackend.expectGET("/api/orders/managed?template=bulk_index;page=1;per_page=500;q[state_not_eq]=canceled;q[completed_at_not_null]=true;q[completed_at_gt]=SomeDate;q[completed_at_lt]=SomeDate").respond "list of orders" + httpBackend.expectGET("/admin/orders/managed?template=bulk_index;page=1;per_page=500;q[state_not_eq]=canceled;q[completed_at_not_null]=true;q[completed_at_gt]=SomeDate;q[completed_at_lt]=SomeDate").respond "list of orders" it "makes a call to dataFetcher, with current start and end date parameters", -> scope.fetchOrders() From c56efabfbecb39bb15bb12c8b9e02556815f5537 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 11:52:49 +1000 Subject: [PATCH 084/151] Removing obsolete rabl templates --- app/views/spree/api/line_items/bulk_show.v1.rabl | 5 ----- app/views/spree/api/orders/bulk_index.v1.rabl | 2 -- app/views/spree/api/orders/bulk_show.v1.rabl | 14 -------------- app/views/spree/api/products/units_show.v1.rabl | 2 -- app/views/spree/api/variants/units_show.v1.rabl | 9 --------- 5 files changed, 32 deletions(-) delete mode 100644 app/views/spree/api/line_items/bulk_show.v1.rabl delete mode 100644 app/views/spree/api/orders/bulk_index.v1.rabl delete mode 100644 app/views/spree/api/orders/bulk_show.v1.rabl delete mode 100644 app/views/spree/api/products/units_show.v1.rabl delete mode 100644 app/views/spree/api/variants/units_show.v1.rabl diff --git a/app/views/spree/api/line_items/bulk_show.v1.rabl b/app/views/spree/api/line_items/bulk_show.v1.rabl deleted file mode 100644 index 8b53c42086..0000000000 --- a/app/views/spree/api/line_items/bulk_show.v1.rabl +++ /dev/null @@ -1,5 +0,0 @@ -object @line_item -attributes :id, :quantity, :max_quantity -node( :supplier ) { |li| partial 'api/enterprises/bulk_show', :object => li.product.supplier } -node( :units_product ) { |li| partial 'spree/api/products/units_show', :object => li.product } -node( :units_variant ) { |li| partial 'spree/api/variants/units_show', :object => li.variant } \ No newline at end of file diff --git a/app/views/spree/api/orders/bulk_index.v1.rabl b/app/views/spree/api/orders/bulk_index.v1.rabl deleted file mode 100644 index 384f518fcf..0000000000 --- a/app/views/spree/api/orders/bulk_index.v1.rabl +++ /dev/null @@ -1,2 +0,0 @@ -collection @orders.order('id ASC') -extends "spree/api/orders/bulk_show" \ No newline at end of file diff --git a/app/views/spree/api/orders/bulk_show.v1.rabl b/app/views/spree/api/orders/bulk_show.v1.rabl deleted file mode 100644 index 9addbbec6f..0000000000 --- a/app/views/spree/api/orders/bulk_show.v1.rabl +++ /dev/null @@ -1,14 +0,0 @@ -object @order -attributes :id, :number - -node( :full_name ) { |order| order.billing_address.nil? ? "" : ( order.billing_address.full_name || "" ) } -node( :email ) { |order| order.email || "" } -node( :phone ) { |order| order.billing_address.nil? ? "a" : ( order.billing_address.phone || "" ) } -node( :completed_at ) { |order| order.completed_at.blank? ? "" : order.completed_at.strftime("%F %T") } -node( :distributor ) { |order| partial 'api/enterprises/bulk_show', :object => order.distributor } -node( :order_cycle ) { |order| partial 'api/order_cycles/bulk_show', :object => order.order_cycle } -node( :line_items ) do |order| - order.line_items.managed_by(@current_api_user).order('id ASC').map do |line_item| - partial 'spree/api/line_items/bulk_show', :object => line_item - end -end \ No newline at end of file diff --git a/app/views/spree/api/products/units_show.v1.rabl b/app/views/spree/api/products/units_show.v1.rabl deleted file mode 100644 index 2108609a33..0000000000 --- a/app/views/spree/api/products/units_show.v1.rabl +++ /dev/null @@ -1,2 +0,0 @@ -object @product -attributes :id, :name, :group_buy_unit_size, :variant_unit \ No newline at end of file diff --git a/app/views/spree/api/variants/units_show.v1.rabl b/app/views/spree/api/variants/units_show.v1.rabl deleted file mode 100644 index 634213e53f..0000000000 --- a/app/views/spree/api/variants/units_show.v1.rabl +++ /dev/null @@ -1,9 +0,0 @@ -object @variant -attributes :id - -node( :unit_text ) do |v| - options_text = v.options_text - v.product.name + (options_text.empty? ? "" : ": #{options_text}") -end - -node( :unit_value ) { |v| v.unit_value } From 8d73b2f53258a00e67c784cd74a2ab423945d39d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 13:47:01 +1000 Subject: [PATCH 085/151] involving... order cycle scopes return distinct OCs --- app/models/order_cycle.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index d16f1d7b8b..88e4953edc 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -70,7 +70,8 @@ class OrderCycle < ActiveRecord::Base # Order cycles where I managed an enterprise at either end of an outgoing exchange # ie. coordinator or distibutor joins(:exchanges).merge(Exchange.outgoing). - where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises) + where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises). + select('DISTINCT order_cycles.*') } scope :involving_managed_producers_of, lambda { |user| @@ -79,7 +80,8 @@ class OrderCycle < ActiveRecord::Base # Order cycles where I managed an enterprise at either end of an incoming exchange # ie. coordinator or producer joins(:exchanges).merge(Exchange.incoming). - where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises) + where('exchanges.receiver_id IN (?) OR exchanges.sender_id IN (?)', enterprises, enterprises). + select('DISTINCT order_cycles.*') } def self.first_opening_for(distributor) From 0a0bb67277ed6ee1572b71fe6317320ad4663444 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 May 2015 14:05:14 +1000 Subject: [PATCH 086/151] No SKU for cloned products. Community topic 175 --- app/models/spree/product_decorator.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 7846a0c1e1..fcaf30c44c 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -108,6 +108,12 @@ Spree::Product.class_eval do # -- Methods + # Called by Spree::Product::duplicate before saving. + def duplicate_extra(parent) + # Spree sets the SKU to "COPY OF #{parent sku}". + self.master.sku = '' + end + def properties_including_inherited # Product properties override producer properties ps = product_properties.all From 05131de1ad65897098f4c5f6125de2daa1af0aef Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 14:11:31 +1000 Subject: [PATCH 087/151] Use full_name on BOM instead of options_text --- app/serializers/api/admin/units_variant_serializer.rb | 8 ++++---- app/views/spree/admin/orders/bulk_management.html.haml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/serializers/api/admin/units_variant_serializer.rb b/app/serializers/api/admin/units_variant_serializer.rb index e96f9299f6..dc27e3adc3 100644 --- a/app/serializers/api/admin/units_variant_serializer.rb +++ b/app/serializers/api/admin/units_variant_serializer.rb @@ -1,8 +1,8 @@ class Api::Admin::UnitsVariantSerializer < ActiveModel::Serializer - attributes :id, :unit_text, :unit_value + attributes :id, :full_name, :unit_value - def unit_text - options_text = object.options_text - object.product.name + (options_text.empty? ? "" : ": #{options_text}") + def full_name + full_name = object.full_name + object.product.name + (full_name.empty? ? "" : ": #{full_name}") end end diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 61bf9fbb7e..0f9bd05d04 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -43,7 +43,7 @@ Shared Resource? %div{ :class => "eight columns" } %h6{ :class => "eight columns alpha", 'ng-show' => 'sharedResource', style: 'text-align: center;' } {{ selectedUnitsProduct.name + ": ALL" }} - %h6{ :class => "eight columns alpha", 'ng-hide' => 'sharedResource', style: 'text-align: center;' } {{ selectedUnitsVariant.unit_text }} + %h6{ :class => "eight columns alpha", 'ng-hide' => 'sharedResource', style: 'text-align: center;' } {{ selectedUnitsVariant.full_name }} %div{ :class => "four columns omega" } %h6{ :class => "four columns alpha", :style => 'text-align: right;' } %a{ :href => '#', 'ng-click' => 'selectedUnitsVariant = {};selectedUnitsProduct = {};sharedResource=false;' } Clear @@ -122,7 +122,7 @@ %th.hub{ 'ng-show' => 'columns.hub.visible' } %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } Hub %th.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'units_variant.unit_text'; reverse = !reverse" } Product: Unit + %a{ :href => '', 'ng-click' => "predicate = 'units_variant.full_name'; reverse = !reverse" } Product: Unit %th.quantity{ 'ng-show' => 'columns.quantity.visible' } Quantity %th.max{ 'ng-show' => 'columns.max.visible' } Max %th.actions @@ -141,7 +141,7 @@ %td.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } {{ line_item.order.order_cycle.name }} %td.hub{ 'ng-show' => 'columns.hub.visible' } {{ line_item.order.distributor.name }} %td.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.unit_text }} + %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.full_name }} %td.quantity{ 'ng-show' => 'columns.quantity.visible' } %input{ :type => 'number', :name => 'quantity', 'ng-model' => "line_item.quantity", 'ofn-line-item-upd-attr' => "quantity" } %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} @@ -149,4 +149,4 @@ %a{ :class => "edit-order icon-edit no-text", 'ofn-confirm-link-path' => "/admin/orders/{{line_item.order.number}}/edit" } %td.actions %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } - %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'pendingChanges.submitAll()' } \ No newline at end of file + %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'pendingChanges.submitAll()' } From 01d4cf6ecf7dc607bd568ee7c51370a1e6115b3a Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 15:02:36 +1000 Subject: [PATCH 088/151] Renaming managed_products permissions method to editable_products --- lib/open_food_network/permissions.rb | 2 +- spec/lib/open_food_network/permissions_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 4da645582d..e207eea797 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -106,7 +106,7 @@ module OpenFoodNetwork Spree::LineItem.where(order_id: editable_orders) end - def managed_products + def editable_products managed_enterprise_products_ids = managed_enterprise_products.pluck :id permitted_enterprise_products_ids = related_enterprise_products.pluck :id Spree::Product.where('id IN (?)', managed_enterprise_products_ids + permitted_enterprise_products_ids) diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 10552572f0..2f19a7a404 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -164,7 +164,7 @@ module OpenFoodNetwork end end - describe "finding managed products" do + describe "finding editable products" do let!(:p1) { create(:simple_product) } let!(:p2) { create(:simple_product) } @@ -175,12 +175,12 @@ module OpenFoodNetwork it "returns products produced by managed enterprises" do permissions.stub(:managed_enterprise_products) { Spree::Product.where(id: p1) } - permissions.managed_products.should == [p1] + permissions.editable_products.should == [p1] end it "returns products produced by permitted enterprises" do permissions.stub(:related_enterprise_products) { Spree::Product.where(id: p2) } - permissions.managed_products.should == [p2] + permissions.editable_products.should == [p2] end end From 6953f6193974f404ee6058a195e73f4100675b1c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 May 2015 15:07:22 +1000 Subject: [PATCH 089/151] bulk product edit: new col "on demand" --- app/assets/javascripts/admin/bulk_product_update.js.coffee | 4 ++++ .../spree/admin/products/bulk_edit/_products_head.html.haml | 2 ++ .../admin/products/bulk_edit/_products_product.html.haml | 2 ++ 3 files changed, 8 insertions(+) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 8cac0abeda..673197ae6d 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -10,6 +10,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout unit: {name: "Unit", visible: true} price: {name: "Price", visible: true} on_hand: {name: "On Hand", visible: true} + on_demand: {name: "On Demand", visible: false} category: {name: "Category", visible: false} inherits_properties: {name: "Inherits Properties?", visible: false} available_on: {name: "Available On", visible: false} @@ -309,6 +310,9 @@ filterSubmitProducts = (productsToFilter) -> if product.hasOwnProperty("on_hand") and filteredVariants.length == 0 #only update if no variants present filteredProduct.on_hand = product.on_hand hasUpdatableProperty = true + if product.hasOwnProperty("on_demand") + filteredProduct.on_demand = product.on_demand + hasUpdatableProperty = true if product.hasOwnProperty("category_id") filteredProduct.primary_taxon_id = product.category_id hasUpdatableProperty = true diff --git a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml index 2e37da2bc8..18bd000473 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml @@ -7,6 +7,7 @@ %col.display_as{ ng: { show: 'columns.unit.visible' } } %col.price{ ng: { show: 'columns.price.visible'} } %col.on_hand{ ng: { show: 'columns.on_hand.visible' } } + %col.on_demand{ ng: { show: 'columns.on_demand.visible' } } %col.category{ ng: { show: 'columns.category.visible' } } %col.inherits_properties{ ng: { show: 'columns.inherits_properties.visible' } } %col.available_on{ ng: { show: 'columns.available_on.visible' } } @@ -24,6 +25,7 @@ %th.display_as{ 'ng-show' => 'columns.unit.visible' } Display As %th.price{ 'ng-show' => 'columns.price.visible' } Price %th.on_hand{ 'ng-show' => 'columns.on_hand.visible' } On Hand + %th.on_demand{ 'ng-show' => 'columns.on_demand.visible' } On Demand %th.category{ 'ng-show' => 'columns.category.visible' } Category %th.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' } Inherits Properties? %th.available_on{ 'ng-show' => 'columns.available_on.visible' } Av. On diff --git a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml index 0eb9ff5f58..48564991dd 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml @@ -20,6 +20,8 @@ %td.on_hand{ 'ng-show' => 'columns.on_hand.visible' } %span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-show' => '!hasOnDemandVariants(product) && (hasVariants(product) || product.on_demand)' } %input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ofn-track-product' => 'on_hand', 'ng-hide' => 'hasVariants(product) || product.on_demand', :type => 'number' } + %td.on_demand{ 'ng-show' => 'columns.on_demand.visible' } + %input.field{ 'ng-model' => 'product.on_demand', :name => 'on_demand', 'ofn-track-product' => 'on_demand', :type => 'checkbox' } %td.category{ 'ng-if' => 'columns.category.visible' } %input.fullwidth{ :type => 'text', id: "p{{product.id}}_category_id", 'ng-model' => 'product.category_id', 'ofn-taxon-autocomplete' => '', 'ofn-track-product' => 'category_id', 'multiple-selection' => 'false', placeholder: 'Category' } %td.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' } From 1e26466d119134a48cf73c5bee87d4215bbb47ee Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 May 2015 15:29:35 +1000 Subject: [PATCH 090/151] bulk product edit: "on demand" for new product --- .../admin/products/new/replace_form.html.haml.deface | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface index 33c70b728e..3fd5a74cb1 100644 --- a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface +++ b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface @@ -38,20 +38,26 @@ .twelve.columns.alpha .six.columns.alpha = render 'spree/admin/products/primary_taxon_form', f: f - .three.columns + .two.columns = f.field_container :price do = f.label :price, t(:price) %span.required * %br/ = f.text_field :price, class: 'fullwidth' = f.error_message_on :price - .three.columns.omega + .two.columns = f.field_container :on_hand do = f.label :on_hand, t(:on_hand) %span.required * %br/ = f.text_field :on_hand, class: 'fullwidth' = f.error_message_on :on_hand + .two.columns.omega + = f.field_container :on_demand do + = f.label :on_demand, t(:on_demand) + %br/ + = f.check_box :on_demand + = f.error_message_on :on_demand .twelve.columns.alpha .six.columns.alpha   .three.columns From 8132f07d8819d41704ceb7286f6aa1d09d2e44fb Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 15:53:10 +1000 Subject: [PATCH 091/151] Adding visible products method to permissions --- lib/open_food_network/permissions.rb | 18 +++++-- .../lib/open_food_network/permissions_spec.rb | 49 +++++++++++++------ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index e207eea797..87baa533ad 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -108,8 +108,18 @@ module OpenFoodNetwork def editable_products managed_enterprise_products_ids = managed_enterprise_products.pluck :id - permitted_enterprise_products_ids = related_enterprise_products.pluck :id - Spree::Product.where('id IN (?)', managed_enterprise_products_ids + permitted_enterprise_products_ids) + permitted_enterprise_products_ids = products_supplied_by( + related_enterprises_granting(:manage_products).pluck(:id) + ).pluck :id + Spree::Product.where('spree_products.id IN (?)', managed_enterprise_products_ids | permitted_enterprise_products_ids) + end + + def visible_products + managed_enterprise_products_ids = managed_enterprise_products.pluck :id + permitted_enterprise_products_ids = products_supplied_by( + related_enterprises_granting(:manage_products).pluck(:id) | related_enterprises_granting(:add_to_order_cycle).pluck(:id) + ).pluck :id + Spree::Product.where('spree_products.id IN (?)', managed_enterprise_products_ids | permitted_enterprise_products_ids) end def managed_product_enterprises @@ -181,8 +191,8 @@ module OpenFoodNetwork Spree::Product.managed_by(@user) end - def related_enterprise_products - Spree::Product.where('supplier_id IN (?)', related_enterprises_granting(:manage_products)) + def products_supplied_by(suppliers) + Spree::Product.where('supplier_id IN (?)', suppliers) end end end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 2f19a7a404..cf2aa1a217 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -165,12 +165,12 @@ module OpenFoodNetwork end describe "finding editable products" do - let!(:p1) { create(:simple_product) } - let!(:p2) { create(:simple_product) } + let!(:p1) { create(:simple_product, supplier: create(:supplier_enterprise) ) } + let!(:p2) { create(:simple_product, supplier: create(:supplier_enterprise) ) } before do permissions.stub(:managed_enterprise_products) { Spree::Product.where('1=0') } - permissions.stub(:related_enterprise_products) { Spree::Product.where('1=0') } + allow(permissions).to receive(:related_enterprises_granting).with(:manage_products) { Enterprise.where("1=0") } end it "returns products produced by managed enterprises" do @@ -179,11 +179,41 @@ module OpenFoodNetwork end it "returns products produced by permitted enterprises" do - permissions.stub(:related_enterprise_products) { Spree::Product.where(id: p2) } + allow(permissions).to receive(:related_enterprises_granting). + with(:manage_products) { Enterprise.where(id: p2.supplier) } permissions.editable_products.should == [p2] end end + describe "finding visible products" do + let!(:p1) { create(:simple_product, supplier: create(:supplier_enterprise) ) } + let!(:p2) { create(:simple_product, supplier: create(:supplier_enterprise) ) } + let!(:p3) { create(:simple_product, supplier: create(:supplier_enterprise) ) } + + before do + permissions.stub(:managed_enterprise_products) { Spree::Product.where("1=0") } + allow(permissions).to receive(:related_enterprises_granting).with(:manage_products) { Enterprise.where("1=0") } + allow(permissions).to receive(:related_enterprises_granting).with(:add_to_order_cycle) { Enterprise.where("1=0") } + end + + it "returns products produced by managed enterprises" do + permissions.stub(:managed_enterprise_products) { Spree::Product.where(id: p1) } + permissions.visible_products.should == [p1] + end + + it "returns products produced by enterprises that have granted manage products" do + allow(permissions).to receive(:related_enterprises_granting). + with(:manage_products) { Enterprise.where(id: p2.supplier) } + permissions.visible_products.should == [p2] + end + + it "returns products produced by enterprises that have granted P-OC" do + allow(permissions).to receive(:related_enterprises_granting). + with(:add_to_order_cycle) { Enterprise.where(id: p3.supplier) } + permissions.visible_products.should == [p3] + end + end + describe "finding enterprises that we manage products for" do let(:e) { double(:enterprise) } @@ -232,17 +262,6 @@ module OpenFoodNetwork end end - describe "finding the supplied products of related enterprises" do - let!(:e) { create(:enterprise) } - let!(:p) { create(:simple_product, supplier: e) } - - it "returns supplied products" do - permissions.should_receive(:related_enterprises_granting).with(:manage_products) { [e] } - - permissions.send(:related_enterprise_products).should == [p] - end - end - describe "finding orders that are visible in reports" do let(:distributor) { create(:distributor_enterprise) } let(:coordinator) { create(:distributor_enterprise) } From 65a6329132739e99634ed261f64bf2a81e4b5e9e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 16:19:08 +1000 Subject: [PATCH 092/151] Products and inventory reports scopes products to visible in permissions --- .../products_and_inventory_report.rb | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/open_food_network/products_and_inventory_report.rb b/lib/open_food_network/products_and_inventory_report.rb index 1cb485efee..163f9a4f50 100644 --- a/lib/open_food_network/products_and_inventory_report.rb +++ b/lib/open_food_network/products_and_inventory_report.rb @@ -9,7 +9,7 @@ module OpenFoodNetwork def header [ - "Supplier", + "Supplier", "Producer Suburb", "Product", "Product Properties", @@ -36,6 +36,16 @@ module OpenFoodNetwork end end + def permissions + return @permissions unless @permissions.nil? + @permissions = OpenFoodNetwork::Permissions.new(@user) + end + + def visible_products + return @visible_products unless @visible_products.nil? + @visible_products = permissions.visible_products + end + def variants filter(child_variants) + filter(master_variants) end @@ -43,7 +53,7 @@ module OpenFoodNetwork def child_variants Spree::Variant.where(:is_master => false) .joins(:product) - .merge(Spree::Product.managed_by(@user)) + .merge(visible_products) .order("spree_products.name") end @@ -53,7 +63,7 @@ module OpenFoodNetwork .where("(select spree_variants.id from spree_variants as other_spree_variants WHERE other_spree_variants.product_id = spree_variants.product_id AND other_spree_variants.is_master = 'f' LIMIT 1) IS NULL") - .merge(Spree::Product.managed_by(@user)) + .merge(visible_products) .order("spree_products.name") end From 3431c687b85aa8f56f73a6908db3c5c7924c3b3a Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 20:54:27 +1000 Subject: [PATCH 093/151] Making sure every created by factories has a distributor --- spec/factories.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/factories.rb b/spec/factories.rb index c798999134..6cfddd85a5 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -238,6 +238,10 @@ FactoryGirl.modify do unit_description '' end + factory :order do + distributor { create(:distributor_enterprise) } + end + factory :shipping_method do distributors { [Enterprise.is_distributor.first || FactoryGirl.create(:distributor_enterprise)] } display_on '' From 1aca4657d9a7e1b7ba1ce9db0c78eb7081cfc095 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 20:59:06 +1000 Subject: [PATCH 094/151] Oops, switch managed_products to editable_products for Api::ProductsController --- app/controllers/spree/api/products_controller_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/spree/api/products_controller_decorator.rb b/app/controllers/spree/api/products_controller_decorator.rb index 6e9799c86d..e809dbc23e 100644 --- a/app/controllers/spree/api/products_controller_decorator.rb +++ b/app/controllers/spree/api/products_controller_decorator.rb @@ -11,7 +11,7 @@ Spree::Api::ProductsController.class_eval do # TODO: This should be named 'managed'. Is the action above used? Maybe we should remove it. def bulk_products - @products = OpenFoodNetwork::Permissions.new(current_api_user).managed_products. + @products = OpenFoodNetwork::Permissions.new(current_api_user).editable_products. merge(product_scope). order('created_at DESC'). ransack(params[:q]).result. From 0b2877136460871cf17a6240a360361e6439d074 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 20 May 2015 21:06:20 +1000 Subject: [PATCH 095/151] Removing unrequired specs for variant/product 'units_show' --- spec/controllers/spree/api/products_controller_spec.rb | 7 ------- spec/controllers/spree/api/variants_controller_spec.rb | 7 ------- 2 files changed, 14 deletions(-) diff --git a/spec/controllers/spree/api/products_controller_spec.rb b/spec/controllers/spree/api/products_controller_spec.rb index eb56859365..312c6a29b4 100644 --- a/spec/controllers/spree/api/products_controller_spec.rb +++ b/spec/controllers/spree/api/products_controller_spec.rb @@ -13,7 +13,6 @@ module Spree let!(:product3) { FactoryGirl.create(:product, supplier: supplier) } let(:product_other_supplier) { FactoryGirl.create(:product, supplier: supplier2) } let(:attributes) { [:id, :name, :supplier, :price, :on_hand, :available_on, :permalink_live] } - let(:unit_attributes) { [:id, :name, :group_buy_unit_size, :variant_unit] } before do stub_authentication! @@ -72,12 +71,6 @@ module Spree attributes.all?{ |attr| keys.include? attr }.should == true end - it "retrieves a list of products with attributes relating to units" do - spree_get :show, { :id => product1.id, :template => "units_show", :format => :json } - keys = json_response.keys.map{ |key| key.to_sym } - unit_attributes.all?{ |attr| keys.include? attr }.should == true - end - it "sorts products in ascending id order" do spree_get :index, { :template => 'bulk_index', :format => :json } ids = json_response.map{ |product| product['id'] } diff --git a/spec/controllers/spree/api/variants_controller_spec.rb b/spec/controllers/spree/api/variants_controller_spec.rb index 5df1a40dde..5fb9f2f2a0 100644 --- a/spec/controllers/spree/api/variants_controller_spec.rb +++ b/spec/controllers/spree/api/variants_controller_spec.rb @@ -9,7 +9,6 @@ module Spree let!(:variant2) { FactoryGirl.create(:variant) } let!(:variant3) { FactoryGirl.create(:variant) } let(:attributes) { [:id, :options_text, :price, :on_hand, :unit_value, :unit_description, :on_demand, :display_as, :display_name] } - let(:unit_attributes) { [:id, :unit_text, :unit_value] } before do stub_authentication! @@ -25,12 +24,6 @@ module Spree attributes.all?{ |attr| keys.include? attr }.should == true end - it "retrieves a list of variants with attributes relating to units" do - spree_get :show, { :id => variant1.id, :template => "units_show", :format => :json } - keys = json_response.keys.map{ |key| key.to_sym } - unit_attributes.all?{ |attr| keys.include? attr }.should == true - end - it "is denied access when trying to delete a variant" do product = create(:product) variant = product.master From 19367670845a9bc63a619bee11dd27af6be283bd Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 21 May 2015 11:06:08 +1000 Subject: [PATCH 096/151] BPE: new col for tax category --- app/assets/javascripts/admin/bulk_product_update.js.coffee | 7 ++++++- app/helpers/admin/injection_helper.rb | 4 ++++ app/serializers/api/admin/product_serializer.rb | 2 +- app/serializers/api/admin/tax_category_serializer.rb | 3 +++ app/views/spree/admin/products/bulk_edit/_data.html.haml | 1 + .../admin/products/bulk_edit/_products_head.html.haml | 2 ++ .../admin/products/bulk_edit/_products_product.html.haml | 3 +++ 7 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 app/serializers/api/admin/tax_category_serializer.rb diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 673197ae6d..5098152551 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $http, BulkProducts, DisplayProperties, dataFetcher, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, SpreeApiAuth) -> +angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $http, BulkProducts, DisplayProperties, dataFetcher, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, SpreeApiAuth, tax_categories) -> $scope.loading = true $scope.StatusMessage = StatusMessage @@ -12,6 +12,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout on_hand: {name: "On Hand", visible: true} on_demand: {name: "On Demand", visible: false} category: {name: "Category", visible: false} + tax_category: {name: "Tax Category", visible: false} inherits_properties: {name: "Inherits Properties?", visible: false} available_on: {name: "Available On", visible: false} @@ -33,6 +34,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.producers = producers $scope.taxons = Taxons.taxons + $scope.tax_categories = tax_categories $scope.filterProducers = [{id: "0", name: ""}].concat $scope.producers $scope.filterTaxons = [{id: "0", name: ""}].concat $scope.taxons $scope.producerFilter = "0" @@ -316,6 +318,9 @@ filterSubmitProducts = (productsToFilter) -> if product.hasOwnProperty("category_id") filteredProduct.primary_taxon_id = product.category_id hasUpdatableProperty = true + if product.hasOwnProperty("tax_category_id") + filteredProduct.tax_category_id = product.tax_category_id + hasUpdatableProperty = true if product.hasOwnProperty("inherits_properties") filteredProduct.inherits_properties = product.inherits_properties hasUpdatableProperty = true diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 36ddccdb9a..961bc60afb 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -50,6 +50,10 @@ module Admin admin_inject_json_ams_array "ofn.admin", "products", @products, Api::Admin::ProductSerializer end + def admin_inject_tax_categories + admin_inject_json_ams_array "ofn.admin", "tax_categories", @tax_categories, Api::Admin::TaxCategorySerializer + end + def admin_inject_taxons admin_inject_json_ams_array "admin.taxons", "taxons", @taxons, Api::Admin::TaxonSerializer end diff --git a/app/serializers/api/admin/product_serializer.rb b/app/serializers/api/admin/product_serializer.rb index b1f233c044..65dd34ba6b 100644 --- a/app/serializers/api/admin/product_serializer.rb +++ b/app/serializers/api/admin/product_serializer.rb @@ -1,7 +1,7 @@ class Api::Admin::ProductSerializer < ActiveModel::Serializer attributes :id, :name, :sku, :variant_unit, :variant_unit_scale, :variant_unit_name, :on_demand, :inherits_properties - attributes :on_hand, :price, :available_on, :permalink_live + attributes :on_hand, :price, :available_on, :permalink_live, :tax_category_id has_one :supplier, key: :producer_id, embed: :id has_one :primary_taxon, key: :category_id, embed: :id diff --git a/app/serializers/api/admin/tax_category_serializer.rb b/app/serializers/api/admin/tax_category_serializer.rb new file mode 100644 index 0000000000..37e99578b8 --- /dev/null +++ b/app/serializers/api/admin/tax_category_serializer.rb @@ -0,0 +1,3 @@ +class Api::Admin::TaxCategorySerializer < ActiveModel::Serializer + attributes :id, :name +end diff --git a/app/views/spree/admin/products/bulk_edit/_data.html.haml b/app/views/spree/admin/products/bulk_edit/_data.html.haml index 8b727be3eb..3624421870 100644 --- a/app/views/spree/admin/products/bulk_edit/_data.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_data.html.haml @@ -1,3 +1,4 @@ = admin_inject_producers = admin_inject_taxons += admin_inject_tax_categories = admin_inject_spree_api_key diff --git a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml index 18bd000473..6e22aef8ff 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml @@ -9,6 +9,7 @@ %col.on_hand{ ng: { show: 'columns.on_hand.visible' } } %col.on_demand{ ng: { show: 'columns.on_demand.visible' } } %col.category{ ng: { show: 'columns.category.visible' } } + %col.tax_category{ ng: { show: 'columns.tax_category.visible' } } %col.inherits_properties{ ng: { show: 'columns.inherits_properties.visible' } } %col.available_on{ ng: { show: 'columns.available_on.visible' } } %col.actions @@ -27,6 +28,7 @@ %th.on_hand{ 'ng-show' => 'columns.on_hand.visible' } On Hand %th.on_demand{ 'ng-show' => 'columns.on_demand.visible' } On Demand %th.category{ 'ng-show' => 'columns.category.visible' } Category + %th.tax_category{ 'ng-show' => 'columns.tax_category.visible' } Tax Category %th.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' } Inherits Properties? %th.available_on{ 'ng-show' => 'columns.available_on.visible' } Av. On %th.actions diff --git a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml index 48564991dd..eab9936558 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml @@ -24,6 +24,9 @@ %input.field{ 'ng-model' => 'product.on_demand', :name => 'on_demand', 'ofn-track-product' => 'on_demand', :type => 'checkbox' } %td.category{ 'ng-if' => 'columns.category.visible' } %input.fullwidth{ :type => 'text', id: "p{{product.id}}_category_id", 'ng-model' => 'product.category_id', 'ofn-taxon-autocomplete' => '', 'ofn-track-product' => 'category_id', 'multiple-selection' => 'false', placeholder: 'Category' } + %td.tax_category{ 'ng-if' => 'columns.tax_category.visible' } + %select.select2{ name: 'product_tax_category_id', 'ofn-track-product' => 'tax_category_id', ng: {model: 'product.tax_category_id', options: 'tax_category.id as tax_category.name for tax_category in tax_categories'} } + %option{value: ''} None %td.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' } %input{ 'ng-model' => 'product.inherits_properties', :name => 'inherits_properties', 'ofn-track-product' => 'inherits_properties', type: "checkbox" } %td.available_on{ 'ng-show' => 'columns.available_on.visible' } From 2ed519ef50b93d1b6c6d0c743533cd6da8fa981b Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 21 May 2015 12:37:10 +1000 Subject: [PATCH 097/151] on_demand checkbox for variants --- app/assets/javascripts/admin/bulk_product_update.js.coffee | 5 ++++- .../admin/products/bulk_edit/_products_product.html.haml | 2 +- .../admin/products/bulk_edit/_products_variant.html.haml | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 5098152551..c902328af4 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -312,7 +312,7 @@ filterSubmitProducts = (productsToFilter) -> if product.hasOwnProperty("on_hand") and filteredVariants.length == 0 #only update if no variants present filteredProduct.on_hand = product.on_hand hasUpdatableProperty = true - if product.hasOwnProperty("on_demand") + if product.hasOwnProperty("on_demand") and filteredVariants.length == 0 #only update if no variants present filteredProduct.on_demand = product.on_demand hasUpdatableProperty = true if product.hasOwnProperty("category_id") @@ -346,6 +346,9 @@ filterSubmitVariant = (variant) -> if variant.hasOwnProperty("on_hand") filteredVariant.on_hand = variant.on_hand hasUpdatableProperty = true + if variant.hasOwnProperty("on_demand") + filteredVariant.on_demand = variant.on_demand + hasUpdatableProperty = true if variant.hasOwnProperty("price") filteredVariant.price = variant.price hasUpdatableProperty = true diff --git a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml index eab9936558..376e88071a 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml @@ -21,7 +21,7 @@ %span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-show' => '!hasOnDemandVariants(product) && (hasVariants(product) || product.on_demand)' } %input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ofn-track-product' => 'on_hand', 'ng-hide' => 'hasVariants(product) || product.on_demand', :type => 'number' } %td.on_demand{ 'ng-show' => 'columns.on_demand.visible' } - %input.field{ 'ng-model' => 'product.on_demand', :name => 'on_demand', 'ofn-track-product' => 'on_demand', :type => 'checkbox' } + %input.field{ 'ng-model' => 'product.on_demand', :name => 'on_demand', 'ofn-track-product' => 'on_demand', :type => 'checkbox', 'ng-hide' => 'hasVariants(product)' } %td.category{ 'ng-if' => 'columns.category.visible' } %input.fullwidth{ :type => 'text', id: "p{{product.id}}_category_id", 'ng-model' => 'product.category_id', 'ofn-taxon-autocomplete' => '', 'ofn-track-product' => 'category_id', 'multiple-selection' => 'false', placeholder: 'Category' } %td.tax_category{ 'ng-if' => 'columns.tax_category.visible' } diff --git a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml index 7e60cbfd4c..757b64e781 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml @@ -15,7 +15,10 @@ %td{ 'ng-show' => 'columns.on_hand.visible' } %input.field{ 'ng-model' => 'variant.on_hand', 'ng-change' => 'updateOnHand(product)', :name => 'variant_on_hand', 'ng-hide' => 'variant.on_demand', 'ofn-track-variant' => 'on_hand', :type => 'number' } %span{ 'ng-bind' => 'variant.on_hand', :name => 'variant_on_hand', 'ng-show' => 'variant.on_demand' } + %td{ 'ng-show' => 'columns.on_demand.visible' } + %input.field{ 'ng-model' => 'variant.on_demand', :name => 'variant_on_demand', 'ofn-track-variant' => 'on_demand', :type => 'checkbox' } %td{ 'ng-show' => 'columns.category.visible' } + %td{ 'ng-show' => 'columns.tax_category.visible' } %td{ 'ng-show' => 'columns.inherits_properties.visible' } %td{ 'ng-show' => 'columns.available_on.visible' } %td.actions From 73884d4f019bebb3ffc0a3df94e1341f1fc81645 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 21 May 2015 13:01:28 +1000 Subject: [PATCH 098/151] BPE: Display notice about variant overrides --- app/models/spree/variant_decorator.rb | 1 + app/serializers/api/admin/variant_serializer.rb | 1 + .../spree/admin/products/bulk_edit/_products_variant.html.haml | 1 + 3 files changed, 3 insertions(+) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 03543793b2..f35927ccf7 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -7,6 +7,7 @@ Spree::Variant.class_eval do has_many :exchange_variants, dependent: :destroy has_many :exchanges, through: :exchange_variants + has_many :variant_overrides attr_accessible :unit_value, :unit_description, :images_attributes, :display_as, :display_name accepts_nested_attributes_for :images diff --git a/app/serializers/api/admin/variant_serializer.rb b/app/serializers/api/admin/variant_serializer.rb index 26ad2d7f37..510f7af333 100644 --- a/app/serializers/api/admin/variant_serializer.rb +++ b/app/serializers/api/admin/variant_serializer.rb @@ -1,6 +1,7 @@ class Api::Admin::VariantSerializer < ActiveModel::Serializer attributes :id, :options_text, :unit_value, :unit_description, :unit_to_display, :on_demand, :display_as, :display_name, :name_to_display attributes :on_hand, :price + has_many :variant_overrides def on_hand object.on_hand.nil? ? 0 : ( object.on_hand.to_f.finite? ? object.on_hand : "On demand" ) diff --git a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml index 757b64e781..853d8e1520 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml @@ -24,5 +24,6 @@ %td.actions %a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text", 'ng-show' => "variantSaved(variant)" } %td.actions + %span.icon-warning-sign.with-tip{ 'ng-if' => 'variant.variant_overrides', title: "This variant has {{variant.variant_overrides.length}} override(s)" } %td.actions %a{ 'ng-click' => 'deleteVariant(product,variant)', :class => "delete-variant icon-trash no-text" } From 79a59e2e812fe6d126bff783fde38620f6cd269c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 21 May 2015 13:34:32 +1000 Subject: [PATCH 099/151] Add order_with_distributor factory --- spec/factories.rb | 8 +-- .../admin/bulk_order_management_spec.rb | 64 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/spec/factories.rb b/spec/factories.rb index 6cfddd85a5..cb4d06b35e 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -183,6 +183,10 @@ FactoryGirl.define do end end + factory :order_with_distributor, :parent => :order do + distributor { create(:distributor_enterprise) } + end + factory :zone_with_member, :parent => :zone do default_tax true @@ -238,10 +242,6 @@ FactoryGirl.modify do unit_description '' end - factory :order do - distributor { create(:distributor_enterprise) } - end - factory :shipping_method do distributors { [Enterprise.is_distributor.first || FactoryGirl.create(:distributor_enterprise)] } display_on '' diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index 1058bebfc0..032acaca4e 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -18,9 +18,9 @@ feature %q{ end context "displaying the list of line items" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o3) { FactoryGirl.create(:order, state: 'address', completed_at: nil ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'address', completed_at: nil ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } let!(:li3) { FactoryGirl.create(:line_item, order: o3 ) } @@ -41,8 +41,8 @@ feature %q{ end context "displaying individual columns" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, bill_address: FactoryGirl.create(:address) ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, bill_address: nil ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, bill_address: FactoryGirl.create(:address) ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, bill_address: nil ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2, product: FactoryGirl.create(:product_with_option_types) ) } @@ -94,7 +94,7 @@ feature %q{ end context "tracking changes" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 5 ) } before :each do @@ -117,7 +117,7 @@ feature %q{ end context "submitting data to the server" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 5 ) } before :each do @@ -141,7 +141,7 @@ feature %q{ admin_user = quick_login_as_admin end - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 5 ) } context "using column display toggle" do @@ -171,7 +171,7 @@ feature %q{ context "supplier filter" do let!(:s1) { create(:supplier_enterprise) } let!(:s2) { create(:supplier_enterprise) } - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, product: create(:product, supplier: s1) ) } let!(:li2) { FactoryGirl.create(:line_item, order: o1, product: create(:product, supplier: s2) ) } @@ -205,8 +205,8 @@ feature %q{ context "distributor filter" do let!(:d1) { create(:distributor_enterprise) } let!(:d2) { create(:distributor_enterprise) } - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: d1 ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: d2 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d1 ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d2 ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -241,8 +241,8 @@ feature %q{ let!(:distributor) { create(:distributor_enterprise) } let!(:oc1) { FactoryGirl.create(:simple_order_cycle, distributors: [distributor]) } let!(:oc2) { FactoryGirl.create(:simple_order_cycle, distributors: [distributor]) } - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, order_cycle: oc1 ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, order_cycle: oc2 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, order_cycle: oc1 ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, order_cycle: oc2 ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -284,8 +284,8 @@ feature %q{ let!(:oc2) { FactoryGirl.create(:simple_order_cycle, suppliers: [s2], distributors: [d2] ) } let!(:p1) { FactoryGirl.create(:product, supplier: s1) } let!(:p2) { FactoryGirl.create(:product, supplier: s2) } - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: d1, order_cycle: oc1 ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: d2, order_cycle: oc2 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d1, order_cycle: oc1 ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d2, order_cycle: oc2 ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, product: p1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2, product: p2 ) } @@ -328,9 +328,9 @@ feature %q{ end context "using quick search" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } let!(:li3) { FactoryGirl.create(:line_item, order: o3 ) } @@ -355,9 +355,9 @@ feature %q{ end context "using date restriction controls" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: (Date.today - 8).strftime("%F %T") ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o3) { FactoryGirl.create(:order, state: 'complete', completed_at: (Date.today + 2).strftime("%F %T") ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: (Date.today - 8).strftime("%F %T") ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: (Date.today + 2).strftime("%F %T") ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2, :quantity => 2 ) } let!(:li3) { FactoryGirl.create(:line_item, order: o3, :quantity => 3 ) } @@ -429,8 +429,8 @@ feature %q{ end context "bulk action controls" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -496,8 +496,8 @@ feature %q{ context "using action buttons" do context "using edit buttons" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -515,8 +515,8 @@ feature %q{ end context "using delete buttons" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -539,13 +539,13 @@ feature %q{ end context "clicking the link on variant name" do - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } let!(:p3) { FactoryGirl.create(:product_with_option_types, group_buy: true, group_buy_unit_size: 5000, variant_unit: "weight", variants: [FactoryGirl.create(:variant, unit_value: 1000)] ) } let!(:v3) { p3.variants.first } - let!(:o3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now ) } + let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } let!(:li3) { FactoryGirl.create(:line_item, order: o3, variant: v3, quantity: 3, max_quantity: 6 ) } let!(:li4) { FactoryGirl.create(:line_item, order: o2, variant: v3, quantity: 1, max_quantity: 3 ) } @@ -605,8 +605,8 @@ feature %q{ let(:s1) { create(:supplier_enterprise, name: 'First Supplier') } let(:d1) { create(:distributor_enterprise, name: 'First Distributor') } let(:d2) { create(:distributor_enterprise, name: 'Another Distributor') } - let!(:o1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: d1 ) } - let!(:o2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: d2 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d1 ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d2 ) } let!(:line_item_distributed) { FactoryGirl.create(:line_item, order: o1, product: create(:product, supplier: s1) ) } let!(:line_item_not_distributed) { FactoryGirl.create(:line_item, order: o2, product: create(:product, supplier: s1) ) } From cd44d43b3ebcea97c2b17387810c094f275f184d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 21 May 2015 17:48:35 +1000 Subject: [PATCH 100/151] Adding price to line_item serializer (oops, forgot to include when merging in master) --- app/serializers/api/admin/line_item_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/serializers/api/admin/line_item_serializer.rb b/app/serializers/api/admin/line_item_serializer.rb index 7e2917acdd..21fde91145 100644 --- a/app/serializers/api/admin/line_item_serializer.rb +++ b/app/serializers/api/admin/line_item_serializer.rb @@ -1,5 +1,5 @@ class Api::Admin::LineItemSerializer < ActiveModel::Serializer - attributes :id, :quantity, :max_quantity, :supplier, :unit_value, :units_product, :units_variant + attributes :id, :quantity, :max_quantity, :supplier, :price, :unit_value, :units_product, :units_variant def supplier Api::Admin::IdNameSerializer.new(object.product.supplier).serializable_hash From f017197221121b65866b7d7c560c0390b39a9aa6 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 21 May 2015 18:00:14 +1000 Subject: [PATCH 101/151] orders list: filter by distributor and order cycle --- .../spree/admin/orders_controller_decorator.rb | 7 +++++++ ...r_and_order_cycle_filter_inputs.html.haml.deface | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 app/overrides/spree/admin/orders/index/add_distributor_and_order_cycle_filter_inputs.html.haml.deface diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index 42e7068afc..ac3d0fa9e0 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -23,6 +23,13 @@ Spree::Admin::OrdersController.class_eval do distributed_by_user(spree_current_user). page(params[:page]). per(params[:per_page] || Spree::Config[:orders_per_page]) + # Filter orders by distributor + if params[:distributor_ids] + @orders = @orders.where(distributor_id: params[:distributor_ids]) + end + if params[:order_cycle_ids] + @orders = @orders.where(order_cycle_id: params[:order_cycle_ids]) + end } } } # Overwrite to use confirm_email_for_customer instead of confirm_email. diff --git a/app/overrides/spree/admin/orders/index/add_distributor_and_order_cycle_filter_inputs.html.haml.deface b/app/overrides/spree/admin/orders/index/add_distributor_and_order_cycle_filter_inputs.html.haml.deface new file mode 100644 index 0000000000..f9e5e54e88 --- /dev/null +++ b/app/overrides/spree/admin/orders/index/add_distributor_and_order_cycle_filter_inputs.html.haml.deface @@ -0,0 +1,13 @@ +/ insert_before "div.clearfix" + +.field-block.alpha.eight.columns + = label_tag nil, t(:distributors) + = select_tag(:distributor_ids, + options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]), + {class: "select2 fullwidth", multiple: true}) + +.field-block.alpha.eight.columns + = label_tag nil, t(:order_cycles) + = select_tag(:order_cycle_ids, + options_for_select(OrderCycle.managed_by(spree_current_user).map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]), + {class: "select2 fullwidth", multiple: true}) From 7e4751cb3ae549fd3d1ad7bac8fd550ad38e9cbb Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 21 May 2015 22:30:23 +1000 Subject: [PATCH 102/151] updating bulk product js spec --- spec/javascripts/unit/bulk_product_update_spec.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index a6fb0fa432..6048814505 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -215,6 +215,7 @@ describe "filtering products for submission to database", -> variant_unit_scale: 1 variant_unit_name: 'loaf' available_on: available_on + tax_category_id: null master_attributes: id: 2 unit_value: 250 From 9c137ccf0faafde9f0c26fd9ad8a1b373dc7220f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 22 May 2015 11:03:21 +1000 Subject: [PATCH 103/151] provide tax_categories in spec --- spec/javascripts/unit/bulk_product_update_spec.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index 6048814505..fb439f1c0c 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -239,6 +239,7 @@ describe "AdminProductEditCtrl", -> module ($provide)-> $provide.value "producers", [] $provide.value "taxons", [] + $provide.value "tax_categories", [] $provide.value 'SpreeApiKey', 'API_KEY' null From d43df862015f2fc7d1a959210572fbca8c6b6554 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 May 2015 12:20:31 +1000 Subject: [PATCH 104/151] Moving conditional logic into ensure_standard_variant --- app/models/spree/product_decorator.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index e9b99c9b7a..82330c61cf 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -34,7 +34,7 @@ Spree::Product.class_eval do validates_presence_of :variant_unit_name, if: -> p { p.variant_unit == 'items' } - after_save :ensure_standard_variant, if: lambda { master.valid? && variants.empty? } + after_save :ensure_standard_variant after_initialize :set_available_on_to_now, :if => :new_record? after_save :update_units after_touch :touch_distributors @@ -211,10 +211,12 @@ Spree::Product.class_eval do end def ensure_standard_variant - variant = self.master.dup - variant.product = self - variant.is_master = false - self.variants << variant + if master.valid? && variants.empty? + variant = self.master.dup + variant.product = self + variant.is_master = false + self.variants << variant + end end # Override Spree's old save_master method and replace it with the most recent method from spree repository From 63f3ede766216c27c460d02f2a440d5fbacb4371 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 May 2015 12:22:05 +1000 Subject: [PATCH 105/151] Prepare master variants for duplication as standard variant by ensuring they have a unit value and that the product has a variant unit --- ...060622_add_standard_variant_to_products.rb | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/db/migrate/20141003060622_add_standard_variant_to_products.rb b/db/migrate/20141003060622_add_standard_variant_to_products.rb index 7e7f752a5d..5242f86686 100644 --- a/db/migrate/20141003060622_add_standard_variant_to_products.rb +++ b/db/migrate/20141003060622_add_standard_variant_to_products.rb @@ -1,9 +1,27 @@ class AddStandardVariantToProducts < ActiveRecord::Migration def up + # Make sure that all products have a variant_unit + Spree::Product.where("variant_unit IS NULL OR variant_unit = ''").update_all(variant_unit: "items", variant_unit_name: "each") + # Find products without any standard variants - products_with_only_master = Spree::Product.includes(:variants).where('spree_variants.id IS NULL') + products_with_only_master = Spree::Product.includes(:variants).where('spree_variants.id IS NULL').select('DISTINCT spree_products.*') products_with_only_master.each do |product| + # Add a unit_value to the master variant if it doesn't have one + if product.unit_value.blank? + if product.variant_unit == "weight" && match = product.unit_description.andand.match(/^(\d+(\.\d*)?)(k?g) ?(.*)$/) + scale = (match[3] == "kg" ? 1000 : 1) + product.unit_value = (match[1].to_i*scale) + product.unit_description = match[4] + product.save! + else + unless product.variant_unit == "items" && product.unit_description.present? + product.unit_value = 1 + product.save! + end + end + end + # Run the callback to add a copy of the master variant as a standard variant product.send(:ensure_standard_variant) From 5b65f67737b93b2c5a1b7f1f2e46d45179cd04c0 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 May 2015 15:02:49 +1000 Subject: [PATCH 106/151] Amending spec expect after_create when we want after_save --- spec/models/spree/product_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index c30a5920ca..9423e2672c 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -116,10 +116,10 @@ module Spree expect(standard_variant.price).to eq product.master.price end - it "only duplicates master variant on create" do - expect(product).to_not receive :ensure_standard_variant + it "only duplicates master with after_save when no standard variants exist" do + expect(product).to receive :ensure_standard_variant product.name = "Something else" - product.save! + expect{product.save!}.to_not change{product.variants.count} end end From 5d47dc2fdb3652d486d32c9422f90126a13c49b1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 May 2015 15:03:21 +1000 Subject: [PATCH 107/151] Sort array so that order doesn't cause spec fail --- spec/lib/open_food_network/permissions_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index cf2aa1a217..9bf5ef2d9d 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -47,7 +47,7 @@ module OpenFoodNetwork expect(permissions).to receive(:managed_enterprises) { Enterprise.where(id: e1) } expect(permissions).to receive(:related_enterprises_granting).with(:some_permission) { Enterprise.where(id: e3) } expect(permissions).to receive(:related_enterprises_granted).with(:some_permission) { Enterprise.where(id: e4) } - expect(permissions.send(:managed_and_related_enterprises_with, :some_permission)).to eq [e1, e3, e4] + expect(permissions.send(:managed_and_related_enterprises_with, :some_permission).sort).to eq [e1, e3, e4].sort end end end From 28dae3c6c6340b49ce65c51bf2c949c1f560ce37 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 22 May 2015 15:23:19 +1000 Subject: [PATCH 108/151] Enterprises cannot add themselves to Groups --- .../admin/enterprises_controller.rb | 5 +++++ .../form/_primary_details.html.haml | 18 +++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index d4b3244525..a0b55ad3c8 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -3,6 +3,7 @@ module Admin before_filter :load_enterprise_set, :only => :index before_filter :load_countries, :except => [:index, :set_sells, :check_permalink] before_filter :load_methods_and_fees, :only => [:new, :edit, :update, :create] + before_filter :load_groups, :only => [:new, :edit, :update, :create] before_filter :load_taxons, :only => [:new, :edit, :update, :create] before_filter :check_can_change_sells, only: :update before_filter :check_can_change_bulk_sells, only: :bulk_update @@ -127,6 +128,10 @@ module Admin @enterprise_fees = EnterpriseFee.managed_by(spree_current_user).for_enterprise(@enterprise).order(:fee_type, :name).all end + def load_groups + @groups = EnterpriseGroup.managed_by(spree_current_user) | @enterprise.groups + end + def load_taxons @taxons = Spree::Taxon.order(:name) end diff --git a/app/views/admin/enterprises/form/_primary_details.html.haml b/app/views/admin/enterprises/form/_primary_details.html.haml index dce5512f40..96f4674912 100644 --- a/app/views/admin/enterprises/form/_primary_details.html.haml +++ b/app/views/admin/enterprises/form/_primary_details.html.haml @@ -5,15 +5,15 @@ %span.required * .eight.columns.omega = f.text_field :name, { placeholder: "eg. Professor Plum's Biodynamic Truffles" } -.row - .alpha.eleven.columns - .three.columns.alpha - = f.label :group_ids, 'Groups' - .with-tip{'data-powertip' => "Select any groups or regions that you are a member of. This will help customers find your enterprise."} - %a What's this? - - .eight.columns.omega - = f.collection_select :group_ids, EnterpriseGroup.all, :id, :name, {}, class: "select2 fullwidth", multiple: true, placeholder: "Start typing to search available groups..." +- if @groups.present? + .row + .alpha.eleven.columns + .three.columns.alpha + = f.label :group_ids, 'Groups' + .with-tip{'data-powertip' => "Select any groups or regions that you are a member of. This will help customers find your enterprise."} + %a What's this? + .eight.columns.omega + = f.collection_select :group_ids, @groups, :id, :name, {}, class: "select2 fullwidth", multiple: true, placeholder: "Start typing to search available groups..." .row .three.columns.alpha From a5482c269f6ca18ead3bf5fe0c510b7f2f465671 Mon Sep 17 00:00:00 2001 From: Rick Giner Date: Sun, 24 May 2015 09:52:34 +1000 Subject: [PATCH 109/151] #541 Fixed issue of showing negative 'more' numbers, and only working for current Hub --- .../stylesheets/darkswarm/hub_node.css.sass | 34 ++++++++++--------- app/views/home/_fat.html.haml | 2 +- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/hub_node.css.sass b/app/assets/stylesheets/darkswarm/hub_node.css.sass index dfe4d42074..94950fa22c 100644 --- a/app/assets/stylesheets/darkswarm/hub_node.css.sass +++ b/app/assets/stylesheets/darkswarm/hub_node.css.sass @@ -65,6 +65,24 @@ .active_table_row:nth-child(2) padding-bottom: 0.75rem + + .producers-list + li.more-producers-link + .less + display: none + a:hover + text-decoration: underline + li.additional-producer + display: none + &.show-more-producers + li.additional-producer + display: block + li.more-producers-link + .more + display: none + .less + display: block + //CURRENT hub (shows selected hub) &.current //overwrites active_table @@ -83,22 +101,6 @@ .active_table_row:first-child .skinny-head background-color: rgba(255,255,255,0.85) - .producers-list - li.more-producers-link - .less - display: none - a:hover - text-decoration: underline - li.additional-producer - display: none - &.show-more-producers - li.additional-producer - display: block - li.more-producers-link - .more - display: none - .less - display: block //INACTIVE - closed hub &.inactive diff --git a/app/views/home/_fat.html.haml b/app/views/home/_fat.html.haml index 092d9e898d..a5543fa2a3 100644 --- a/app/views/home/_fat.html.haml +++ b/app/views/home/_fat.html.haml @@ -26,7 +26,7 @@ %enterprise-modal %i.ofn-i_036-producers %span{"bo-text" => "enterprise.name"} - %li{"data-is-link" => "true", "class" => "more-producers-link"} + %li{"data-is-link" => "true", "class" => "more-producers-link", "bo-show" => "hub.producers.length>7"} %a{"ng-click" => "toggleMoreProducers=!toggleMoreProducers"} .more + From 99cb09c6f75bfc0889ccac2e9ec09f6958e45b44 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 20 May 2015 14:05:41 +1000 Subject: [PATCH 110/151] When loading products for shopfront, load all variants in one go --- app/controllers/shop_controller.rb | 28 ++++++++++++++++++--- app/serializers/api/product_serializer.rb | 14 +++-------- spec/controllers/shop_controller_spec.rb | 14 +++++++++++ spec/serializers/product_serializer_spec.rb | 14 ----------- 4 files changed, 42 insertions(+), 28 deletions(-) delete mode 100644 spec/serializers/product_serializer_spec.rb diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index 73861def5c..35aece5e1d 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -10,12 +10,15 @@ class ShopController < BaseController end def products - # Can we make this query less slow? - # if @products = products_for_shop + render status: 200, - json: ActiveModel::ArraySerializer.new(@products, each_serializer: Api::ProductSerializer, - current_order_cycle: current_order_cycle, current_distributor: current_distributor).to_json + json: ActiveModel::ArraySerializer.new(@products, + each_serializer: Api::ProductSerializer, + current_order_cycle: current_order_cycle, + current_distributor: current_distributor, + variants: variants_for_shop_by_id).to_json + else render json: "", status: 404 end @@ -46,6 +49,23 @@ class ShopController < BaseController end end + def variants_for_shop_by_id + # We use the in_stock? method here instead of the in_stock scope because we need to + # look up the stock as overridden by VariantOverrides, and the scope method is not affected + # by them. + variants = Spree::Variant. + where(is_master: false). + for_distribution(current_order_cycle, current_distributor). + each { |v| v.scope_to_hub current_distributor }. + select(&:in_stock?) + + variants.inject({}) do |vs, v| + vs[v.product_id] ||= [] + vs[v.product_id] << v + vs + end + end + def taxon_order if current_distributor.preferred_shopfront_taxon_order.present? current_distributor diff --git a/app/serializers/api/product_serializer.rb b/app/serializers/api/product_serializer.rb index 0de794796b..b707aa281c 100644 --- a/app/serializers/api/product_serializer.rb +++ b/app/serializers/api/product_serializer.rb @@ -30,8 +30,9 @@ class Api::CachedProductSerializer < ActiveModel::Serializer #cached #delegate :cache_key, to: :object - attributes :id, :name, :permalink, :count_on_hand, :on_demand, :group_buy, - :notes, :description, :properties_with_values + attributes :id, :name, :permalink, :count_on_hand + attributes :on_demand, :group_buy, :notes, :description + attributes :properties_with_values has_many :variants, serializer: Api::VariantSerializer has_many :taxons, serializer: Api::IdSerializer @@ -46,13 +47,6 @@ class Api::CachedProductSerializer < ActiveModel::Serializer end def variants - # We use the in_stock? method here instead of the in_stock scope because we need to - # look up the stock as overridden by VariantOverrides, and the scope method is not affected - # by them. - - object.variants. - for_distribution(options[:current_order_cycle], options[:current_distributor]). - each { |v| v.scope_to_hub options[:current_distributor] }. - select(&:in_stock?) + options[:variants][object.id] || [] end end diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index 4b7c53da19..10f0eed2cd 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -175,4 +175,18 @@ describe ShopController do end end end + + describe "loading variants" do + let(:hub) { create(:distributor_enterprise) } + let(:oc) { create(:simple_order_cycle, distributors: [hub], variants: [v1]) } + let(:p) { create(:simple_product) } + let!(:v1) { create(:variant, product: p, unit_value: 3) } + let!(:v2) { create(:variant, product: p, unit_value: 5) } + + it "scopes variants to distribution" do + controller.stub(:current_order_cycle) { oc } + controller.stub(:current_distributor) { hub } + controller.send(:variants_for_shop_by_id).should == {p.id => [v1]} + end + end end diff --git a/spec/serializers/product_serializer_spec.rb b/spec/serializers/product_serializer_spec.rb deleted file mode 100644 index 0091668dbb..0000000000 --- a/spec/serializers/product_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe Api::ProductSerializer do - let(:hub) { create(:distributor_enterprise) } - let(:oc) { create(:simple_order_cycle, distributors: [hub], variants: [v1]) } - let(:p) { create(:simple_product) } - let!(:v1) { create(:variant, product: p, unit_value: 3) } - let!(:v2) { create(:variant, product: p, unit_value: 5) } - - it "scopes variants to distribution" do - s = Api::ProductSerializer.new p, current_distributor: hub, current_order_cycle: oc - json = s.to_json - json.should include v1.options_text - json.should_not include v2.options_text - end -end From c5f00d87bd3b63a750f5282583084b84e6e31df6 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 20 May 2015 15:22:01 +1000 Subject: [PATCH 111/151] When loading products for shopfront, load all master variants in one go --- app/controllers/shop_controller.rb | 46 ++++++++++++++--------- app/serializers/api/product_serializer.rb | 5 +++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index 35aece5e1d..0655624a0b 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -17,7 +17,8 @@ class ShopController < BaseController each_serializer: Api::ProductSerializer, current_order_cycle: current_order_cycle, current_distributor: current_distributor, - variants: variants_for_shop_by_id).to_json + variants: variants_for_shop_by_id, + master_variants: master_variants_for_shop_by_id).to_json else render json: "", status: 404 @@ -49,23 +50,6 @@ class ShopController < BaseController end end - def variants_for_shop_by_id - # We use the in_stock? method here instead of the in_stock scope because we need to - # look up the stock as overridden by VariantOverrides, and the scope method is not affected - # by them. - variants = Spree::Variant. - where(is_master: false). - for_distribution(current_order_cycle, current_distributor). - each { |v| v.scope_to_hub current_distributor }. - select(&:in_stock?) - - variants.inject({}) do |vs, v| - vs[v.product_id] ||= [] - vs[v.product_id] << v - vs - end - end - def taxon_order if current_distributor.preferred_shopfront_taxon_order.present? current_distributor @@ -76,4 +60,30 @@ class ShopController < BaseController "name ASC" end end + + def all_variants_for_shop + # We use the in_stock? method here instead of the in_stock scope because we need to + # look up the stock as overridden by VariantOverrides, and the scope method is not affected + # by them. + Spree::Variant. + for_distribution(current_order_cycle, current_distributor). + each { |v| v.scope_to_hub current_distributor }. + select(&:in_stock?) + end + + def variants_for_shop_by_id + index_by_product_id all_variants_for_shop.reject(&:is_master) + end + + def master_variants_for_shop_by_id + index_by_product_id all_variants_for_shop.select(&:is_master) + end + + def index_by_product_id(variants) + variants.inject({}) do |vs, v| + vs[v.product_id] ||= [] + vs[v.product_id] << v + vs + end + end end diff --git a/app/serializers/api/product_serializer.rb b/app/serializers/api/product_serializer.rb index b707aa281c..4c4aec2310 100644 --- a/app/serializers/api/product_serializer.rb +++ b/app/serializers/api/product_serializer.rb @@ -49,4 +49,9 @@ class Api::CachedProductSerializer < ActiveModel::Serializer def variants options[:variants][object.id] || [] end + + def master + options[:master_variants][object.id].andand.first + end + end From 769edbe9d52aebbb6cef71c3a06e90069f789b29 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 May 2015 12:04:59 +1000 Subject: [PATCH 112/151] Find the earliest closing times for each distributor in an active order cycle --- app/models/order_cycle.rb | 16 +++++++++++++++- spec/models/order_cycle_spec.rb | 31 +++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 88e4953edc..dfd45230b6 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -92,11 +92,25 @@ class OrderCycle < ActiveRecord::Base with_distributor(distributor).soonest_closing.first end - def self.most_recently_closed_for(distributor) with_distributor(distributor).most_recently_closed.first end + # Find the earliest closing times for each distributor in an active order cycle, and return + # them in the format {distributor_id => closing_time, ...} + def self.earliest_closing_times + Hash[ + Exchange. + outgoing. + joins(:order_cycle). + merge(OrderCycle.active). + group('exchanges.receiver_id'). + select('exchanges.receiver_id AS receiver_id, MIN(order_cycles.orders_close_at) AS earliest_close_at'). + map { |ex| [ex.receiver_id, ex.earliest_close_at.to_time] } + ] + end + + def clone! oc = self.dup oc.name = "COPY OF #{oc.name}" diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 817d2e8e93..507f9977b1 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -313,7 +313,7 @@ describe OrderCycle do @oc.pickup_time_for(@d2).should == '2-8pm Friday' end end - + describe "finding pickup instructions for a distributor" do it "returns the pickup instructions" do @oc.pickup_instructions_for(@d1).should == "Come get it!" @@ -375,7 +375,7 @@ describe OrderCycle do occ.coordinator_fee_ids.should_not be_empty occ.coordinator_fee_ids.should == oc.coordinator_fee_ids - + # to_h gives us a unique hash for each exchange # check that the clone has no additional exchanges occ.exchanges.map(&:to_h).all? do |ex| @@ -402,7 +402,7 @@ describe OrderCycle do describe "finding order cycles opening in the future" do it "should give the soonest opening order cycle for a distributor" do distributor = create(:distributor_enterprise) - oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 10.days.from_now, orders_close_at: 11.days.from_now) + oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 10.days.from_now, orders_close_at: 11.days.from_now) OrderCycle.first_opening_for(distributor).should == oc end @@ -411,13 +411,32 @@ describe OrderCycle do OrderCycle.first_opening_for(distributor).should == nil end end - + describe "finding open order cycles" do it "should give the soonest closing order cycle for a distributor" do distributor = create(:distributor_enterprise) - oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 1.days.ago, orders_close_at: 11.days.from_now) - oc2 = create(:simple_order_cycle, name: 'oc 2', distributors: [distributor], orders_open_at: 2.days.ago, orders_close_at: 12.days.from_now) + oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 1.days.ago, orders_close_at: 11.days.from_now) + oc2 = create(:simple_order_cycle, name: 'oc 2', distributors: [distributor], orders_open_at: 2.days.ago, orders_close_at: 12.days.from_now) OrderCycle.first_closing_for(distributor).should == oc end end + + describe "finding the earliest closing times for each distributor" do + let(:time1) { 1.week.from_now } + let(:time2) { 2.weeks.from_now } + let(:time3) { 3.weeks.from_now } + let(:e1) { create(:distributor_enterprise) } + let(:e2) { create(:distributor_enterprise) } + let!(:oc1) { create(:simple_order_cycle, orders_close_at: time1, distributors: [e1]) } + let!(:oc2) { create(:simple_order_cycle, orders_close_at: time2, distributors: [e2]) } + let!(:oc3) { create(:simple_order_cycle, orders_close_at: time3, distributors: [e2]) } + + it "returns the closing time, indexed by enterprise id" do + OrderCycle.earliest_closing_times[e1.id].should == time1 + end + + it "returns the earliest closing time" do + OrderCycle.earliest_closing_times[e2.id].should == time2 + end + end end From f940984ca311313e3457da6ec98eb5c044c609f0 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 May 2015 12:07:11 +1000 Subject: [PATCH 113/151] Pull earliest closing time computations out of the serialization loop --- app/helpers/injection_helper.rb | 2 +- app/serializers/api/enterprise_serializer.rb | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 37794cef9d..a17d6e81f9 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -1,6 +1,6 @@ module InjectionHelper def inject_enterprises - inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, active_distributors: @active_distributors + inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, active_distributors: @active_distributors, earliest_closing_times: OrderCycle.earliest_closing_times end def inject_current_order diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 532887ae01..626fe77cad 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -18,14 +18,12 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer attributes :orders_close_at, :active def orders_close_at - OrderCycle.first_closing_for(object).andand.orders_close_at + options[:earliest_closing_times][object.id] end def active - @options[:active_distributors].andand.include? object + options[:active_distributors].andand.include? object end - - end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer From f0e909c92bc7a029ca9fb607accfaa9b4799c871 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 May 2015 12:38:33 +1000 Subject: [PATCH 114/151] Look up the shipping services (pickup, delivery) that different hubs provide --- app/models/spree/shipping_method_decorator.rb | 16 +++++++++++ spec/models/spree/shipping_method_spec.rb | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/app/models/spree/shipping_method_decorator.rb b/app/models/spree/shipping_method_decorator.rb index 4a2cf75b47..b8be603048 100644 --- a/app/models/spree/shipping_method_decorator.rb +++ b/app/models/spree/shipping_method_decorator.rb @@ -25,6 +25,22 @@ Spree::ShippingMethod.class_eval do scope :by_name, order('spree_shipping_methods.name ASC') + + # Return the services (pickup, delivery) that different distributors provide, in the format: + # {distributor_id => {pickup: true, delivery: false}, ...} + def self.services + Hash[ + Spree::ShippingMethod. + joins(:distributor_shipping_methods). + group('distributor_id'). + select("distributor_id"). + select("BOOL_OR(spree_shipping_methods.require_ship_address = 'f') AS pickup"). + select("BOOL_OR(spree_shipping_methods.require_ship_address = 't') AS delivery"). + map { |sm| [sm.distributor_id.to_i, {pickup: sm.pickup == 't', delivery: sm.delivery == 't'}] } + ] + end + + def available_to_order_with_distributor_check?(order, display_on=nil) available_to_order_without_distributor_check?(order, display_on) && self.distributors.include?(order.distributor) diff --git a/spec/models/spree/shipping_method_spec.rb b/spec/models/spree/shipping_method_spec.rb index d6b821e890..8b7f397191 100644 --- a/spec/models/spree/shipping_method_spec.rb +++ b/spec/models/spree/shipping_method_spec.rb @@ -55,5 +55,32 @@ module Spree sm.should be_available_to_order o end end + + describe "finding services offered by all distributors" do + let!(:d1) { create(:distributor_enterprise) } + let!(:d2) { create(:distributor_enterprise) } + let!(:d3) { create(:distributor_enterprise) } + let!(:d4) { create(:distributor_enterprise) } + let!(:d1_pickup) { create(:shipping_method, require_ship_address: false, distributors: [d1]) } + let!(:d1_delivery) { create(:shipping_method, require_ship_address: true, distributors: [d1]) } + let!(:d2_pickup) { create(:shipping_method, require_ship_address: false, distributors: [d2]) } + let!(:d3_delivery) { create(:shipping_method, require_ship_address: true, distributors: [d3]) } + + it "reports when the services are available" do + ShippingMethod.services[d1.id].should == {pickup: true, delivery: true} + end + + it "reports when only pickup is available" do + ShippingMethod.services[d2.id].should == {pickup: true, delivery: false} + end + + it "reports when only delivery is available" do + ShippingMethod.services[d3.id].should == {pickup: false, delivery: true} + end + + it "returns no entry when no service is available" do + ShippingMethod.services[d4.id].should be_nil + end + end end end From ee8db23fd99e08dc4037b8feef03079253276e77 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 May 2015 12:40:04 +1000 Subject: [PATCH 115/151] Pull shipping method service computations out of the serialization loop --- app/helpers/injection_helper.rb | 2 +- app/serializers/api/enterprise_serializer.rb | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index a17d6e81f9..39831711ff 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -1,6 +1,6 @@ module InjectionHelper def inject_enterprises - inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, active_distributors: @active_distributors, earliest_closing_times: OrderCycle.earliest_closing_times + inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, active_distributors: @active_distributors, earliest_closing_times: OrderCycle.earliest_closing_times, shipping_method_services: Spree::ShippingMethod.services end def inject_current_order diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 626fe77cad..f7bd06f239 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -42,11 +42,13 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer has_one :address, serializer: Api::AddressSerializer def pickup - object.shipping_methods.where(:require_ship_address => false).present? + services = options[:shipping_method_services][object.id] + services ? services[:pickup] : false end def delivery - object.shipping_methods.where(:require_ship_address => true).present? + services = options[:shipping_method_services][object.id] + services ? services[:delivery] : false end def email From 704955a1854fbf2739779e5a5a6f6f2845535bba Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 May 2015 15:47:52 +1000 Subject: [PATCH 116/151] Load active distributors where they're needed rather than in most controllers --- app/controllers/base_controller.rb | 3 --- app/controllers/checkout_controller.rb | 3 --- app/controllers/enterprises_controller.rb | 2 +- app/controllers/groups_controller.rb | 1 - app/controllers/home_controller.rb | 4 +--- app/controllers/map_controller.rb | 1 - app/controllers/producers_controller.rb | 3 +-- app/helpers/injection_helper.rb | 18 +++++++++++++++++- app/views/groups/show.html.haml | 6 +++--- spec/controllers/base_controller_spec.rb | 5 ----- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index 1d74df5706..392e2fef64 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -12,9 +12,6 @@ class BaseController < ApplicationController before_filter :check_order_cycle_expiry - def load_active_distributors - @active_distributors ||= Enterprise.distributors_with_active_order_cycles - end private diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 0c42ceb987..11be28ef04 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -12,9 +12,6 @@ class CheckoutController < Spree::CheckoutController include EnterprisesHelper def edit - # Because this controller doesn't inherit from our BaseController - # We need to duplicate the code here - @active_distributors ||= Enterprise.distributors_with_active_order_cycles end def update diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index 097139556c..75ad5c475b 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -4,7 +4,7 @@ class EnterprisesController < BaseController include OrderCyclesHelper # These prepended filters are in the reverse order of execution - prepend_before_filter :load_active_distributors, :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop + prepend_before_filter :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop before_filter :clean_permalink, only: :check_permalink respond_to :js, only: :permalink_checker diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 8653131b5a..6930632966 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,6 +1,5 @@ class GroupsController < BaseController layout 'darkswarm' - before_filter :load_active_distributors def index @groups = EnterpriseGroup.on_front_page.by_position diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 76e179ed22..3bb7a68538 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,11 +1,9 @@ class HomeController < BaseController layout 'darkswarm' - before_filter :load_active_distributors - + def index end def about_us end end - diff --git a/app/controllers/map_controller.rb b/app/controllers/map_controller.rb index a980ba8f40..46a6f5852a 100644 --- a/app/controllers/map_controller.rb +++ b/app/controllers/map_controller.rb @@ -1,6 +1,5 @@ class MapController < BaseController layout 'darkswarm' - before_filter :load_active_distributors def index end diff --git a/app/controllers/producers_controller.rb b/app/controllers/producers_controller.rb index b101a95b7f..42d1d401e5 100644 --- a/app/controllers/producers_controller.rb +++ b/app/controllers/producers_controller.rb @@ -1,7 +1,6 @@ class ProducersController < BaseController layout 'darkswarm' - before_filter :load_active_distributors - + def index end end diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 39831711ff..2229888f98 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -1,6 +1,10 @@ module InjectionHelper def inject_enterprises - inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, active_distributors: @active_distributors, earliest_closing_times: OrderCycle.earliest_closing_times, shipping_method_services: Spree::ShippingMethod.services + inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, enterprise_injection_data + end + + def inject_group_enterprises + inject_json_ams "group_enterprises", @group.enterprises, Api::EnterpriseSerializer, enterprise_injection_data end def inject_current_order @@ -53,4 +57,16 @@ module InjectionHelper end render partial: "json/injection_ams", locals: {name: name, json: json} end + + + private + + def enterprise_injection_data + @active_distributors ||= Enterprise.distributors_with_active_order_cycles + @earliest_closing_times ||= OrderCycle.earliest_closing_times + @shipping_method_services ||= Spree::ShippingMethod.services + + {active_distributors: @active_distributors, earliest_closing_times: @earliest_closing_times, shipping_method_services: @shipping_method_services} + end + end diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 1bc965dfb3..80101d9e8c 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -3,8 +3,8 @@ = inject_enterprises -# inject enterprises in this group --# further hubs and producers of these enterprises can't be resoleved within this small subset -= inject_json_ams "group_enterprises", @group.enterprises, Api::EnterpriseSerializer, active_distributors: @active_distributors +-# further hubs and producers of these enterprises can't be resolved within this small subset += inject_group_enterprises #group-page.row.pad-top{"ng-controller" => "GroupPageCtrl"} .small-12.columns.pad-top @@ -95,7 +95,7 @@ = render partial: 'home/fat' = render partial: 'shared/components/enterprise_no_results' - + .small-12.medium-12.large-3.columns = render partial: 'contact' diff --git a/spec/controllers/base_controller_spec.rb b/spec/controllers/base_controller_spec.rb index 1040b0594c..b5ef006c5b 100644 --- a/spec/controllers/base_controller_spec.rb +++ b/spec/controllers/base_controller_spec.rb @@ -24,9 +24,4 @@ describe BaseController do response.should redirect_to root_url flash[:info].should == "The order cycle you've selected has just closed. Please try again!" end - - it "loads active_distributors" do - Enterprise.stub(:distributors_with_active_order_cycles) { 'active distributors' } - controller.load_active_distributors.should == 'active distributors' - end end From 4a59c85b3e83bcf02837c3665f911f90f2072411 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 May 2015 15:48:40 +1000 Subject: [PATCH 117/151] Inject current hub from AMS rather than RABL --- app/helpers/injection_helper.rb | 4 ++++ app/views/json/_current_hub.rabl | 6 ------ app/views/layouts/darkswarm.html.haml | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) delete mode 100644 app/views/json/_current_hub.rabl diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 2229888f98..a0aa2f87f1 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -7,6 +7,10 @@ module InjectionHelper inject_json_ams "group_enterprises", @group.enterprises, Api::EnterpriseSerializer, enterprise_injection_data end + def inject_current_hub + inject_json_ams "currentHub", current_distributor, Api::EnterpriseSerializer, enterprise_injection_data + end + def inject_current_order inject_json_ams "currentOrder", current_order, Api::CurrentOrderSerializer, current_distributor: current_distributor, current_order_cycle: current_order_cycle end diff --git a/app/views/json/_current_hub.rabl b/app/views/json/_current_hub.rabl deleted file mode 100644 index 103baf9fb3..0000000000 --- a/app/views/json/_current_hub.rabl +++ /dev/null @@ -1,6 +0,0 @@ -object current_distributor -extends 'json/partials/enterprise' - -child suppliers: :producers do - attributes :id -end diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index a627ba4896..5169efa942 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -24,7 +24,7 @@ = render partial: "shared/ie_warning" = javascript_include_tag "iehack" - = inject_json "currentHub", "current_hub" + = inject_current_hub = inject_json "user", "current_user" = inject_json "railsFlash", "flash" = inject_taxons From cf79b90044c219757dc5e2044b9ea1e70944e3b1 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 May 2015 16:24:13 +1000 Subject: [PATCH 118/151] Load relatives of all enterprises in one go --- app/models/enterprise_relationship.rb | 22 +++++++++++++++++++++ spec/models/enterprise_relationship_spec.rb | 12 +++++++++++ 2 files changed, 34 insertions(+) diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index fbdef9d52c..22d04a6cd1 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -25,6 +25,28 @@ class EnterpriseRelationship < ActiveRecord::Base scope :by_name, with_enterprises.order('child_enterprises.name, parent_enterprises.name') + # Load an array of the relatives of each enterprise (ie. any enterprise related to it in + # either direction). This array is split into distributors and producers, and has the format: + # {enterprise_id => {distributors: [id, ...], producers: [id, ...]} } + def self.relatives + relationships = EnterpriseRelationship.includes(:child, :parent) + relatives = {} + + relationships.each do |r| + relatives[r.parent_id] ||= {distributors: [], producers: []} + relatives[r.child_id] ||= {distributors: [], producers: []} + + relatives[r.parent_id][:producers] << r.child_id if r.child.is_primary_producer + relatives[r.parent_id][:distributors] << r.child_id if r.child.is_distributor + + relatives[r.child_id][:producers] << r.parent_id if r.parent.is_primary_producer + relatives[r.child_id][:distributors] << r.parent_id if r.parent.is_distributor + end + + relatives + end + + def permissions_list=(perms) perms.andand.each { |name| permissions.build name: name } end diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb index e87c204037..cb1743dec1 100644 --- a/spec/models/enterprise_relationship_spec.rb +++ b/spec/models/enterprise_relationship_spec.rb @@ -69,4 +69,16 @@ describe EnterpriseRelationship do EnterpriseRelationship.with_permission('two').sort.should == [er1, er2].sort end end + + describe "finding relatives" do + let(:e1) { create(:supplier_enterprise) } + let(:e2) { create(:supplier_enterprise, sells: 'any') } + let!(:er) { create(:enterprise_relationship, parent: e1, child: e2) } + + it "categorises enterprises into distributors and producers" do + EnterpriseRelationship.relatives.should == + {e1.id => {distributors: [e2.id], producers: [e2.id]}, + e2.id => {distributors: [], producers: [e1.id]}} + end + end end From 3afd636577ad2348687a098b01c0dec4d457126e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 May 2015 16:25:05 +1000 Subject: [PATCH 119/151] Pull relatives computation out of the serialization loop --- app/helpers/injection_helper.rb | 3 ++- app/serializers/api/enterprise_serializer.rb | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index a0aa2f87f1..1d5f4d19e2 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -69,8 +69,9 @@ module InjectionHelper @active_distributors ||= Enterprise.distributors_with_active_order_cycles @earliest_closing_times ||= OrderCycle.earliest_closing_times @shipping_method_services ||= Spree::ShippingMethod.services + @relatives ||= EnterpriseRelationship.relatives - {active_distributors: @active_distributors, earliest_closing_times: @earliest_closing_times, shipping_method_services: @shipping_method_services} + {active_distributors: @active_distributors, earliest_closing_times: @earliest_closing_times, shipping_method_services: @shipping_method_services, relatives: @relatives} end end diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index f7bd06f239..a97544b3ee 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -72,11 +72,13 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer end def producers - ActiveModel::ArraySerializer.new(object.suppliers.activated, {each_serializer: Api::IdSerializer}) + relatives = options[:relatives][object.id] + relatives ? relatives[:producers] : [] end def hubs - ActiveModel::ArraySerializer.new(object.distributors.activated, {each_serializer: Api::IdSerializer}) + relatives = options[:relatives][object.id] + relatives ? relatives[:distributors] : [] end # Map svg icons. From 2c92b5a7516b888d87583ad6e35a876bc583d504 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 22 May 2015 10:57:14 +1000 Subject: [PATCH 120/151] Find all supplied and distributed taxons --- app/models/spree/taxon_decorator.rb | 36 +++++++++++++++++++++++++++++ spec/models/spree/taxon_spec.rb | 28 ++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 spec/models/spree/taxon_spec.rb diff --git a/app/models/spree/taxon_decorator.rb b/app/models/spree/taxon_decorator.rb index 10ee0b4719..1a26ce73a8 100644 --- a/app/models/spree/taxon_decorator.rb +++ b/app/models/spree/taxon_decorator.rb @@ -9,4 +9,40 @@ Spree::Taxon.class_eval do #fs << Spree::ProductFilters.distributor_filter if Spree::ProductFilters.respond_to? :distributor_filter fs end + + # Find all the taxons of supplied products for each enterprise, indexed by enterprise. + # Format: {enterprise_id => [taxon_id, ...]} + def self.supplied_taxons + taxons = {} + + Spree::Taxon. + joins(:products => :supplier). + select('spree_taxons.*, enterprises.id AS enterprise_id'). + each do |t| + + taxons[t.enterprise_id.to_i] ||= Set.new + taxons[t.enterprise_id.to_i] << t.id + end + + taxons + end + + # Find all the taxons of distributed products for each enterprise, indexed by enterprise. + # Format: {enterprise_id => [taxon_id, ...]} + def self.distributed_taxons + taxons = {} + + Spree::Taxon. + joins(:products). + merge(Spree::Product.with_order_cycles_outer). + where('o_exchanges.incoming = ?', false). + select('spree_taxons.*, o_exchanges.receiver_id AS enterprise_id'). + each do |t| + + taxons[t.enterprise_id.to_i] ||= Set.new + taxons[t.enterprise_id.to_i] << t.id + end + + taxons + end end diff --git a/spec/models/spree/taxon_spec.rb b/spec/models/spree/taxon_spec.rb new file mode 100644 index 0000000000..a0d729c054 --- /dev/null +++ b/spec/models/spree/taxon_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +module Spree + describe Taxon do + let(:e) { create(:supplier_enterprise) } + let(:t0) { p1.taxons.order('id ASC').first } + let(:t1) { create(:taxon) } + let(:t2) { create(:taxon) } + + describe "finding all supplied taxons" do + let!(:p1) { create(:simple_product, supplier: e, taxons: [t1, t2]) } + + it "finds taxons" do + Taxon.supplied_taxons.should == {e.id => Set.new([t0.id, t1.id, t2.id])} + end + end + + describe "finding all distributed taxons" do + let!(:oc) { create(:simple_order_cycle, distributors: [e], variants: [p1.master]) } + let(:s) { create(:supplier_enterprise) } + let(:p1) { create(:simple_product, supplier: s, taxons: [t1, t2]) } + + it "finds taxons" do + Taxon.distributed_taxons.should == {e.id => Set.new([t0.id, t1.id, t2.id])} + end + end + end +end From 1a887df4127d932f2f0f40d54a4ffc9f416aa65b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 22 May 2015 11:03:53 +1000 Subject: [PATCH 121/151] Pull taxon computation out of the serialization loop --- app/helpers/injection_helper.rb | 4 +++- app/serializers/api/enterprise_serializer.rb | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 1d5f4d19e2..c2e4e6c91a 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -70,8 +70,10 @@ module InjectionHelper @earliest_closing_times ||= OrderCycle.earliest_closing_times @shipping_method_services ||= Spree::ShippingMethod.services @relatives ||= EnterpriseRelationship.relatives + @supplied_taxons ||= Spree::Taxon.supplied_taxons + @distributed_taxons ||= Spree::Taxon.distributed_taxons - {active_distributors: @active_distributors, earliest_closing_times: @earliest_closing_times, shipping_method_services: @shipping_method_services, relatives: @relatives} + {active_distributors: @active_distributors, earliest_closing_times: @earliest_closing_times, shipping_method_services: @shipping_method_services, relatives: @relatives, supplied_taxons: @supplied_taxons, distributed_taxons: @distributed_taxons} end end diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index a97544b3ee..d15ee7860a 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -36,11 +36,18 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer :email, :hash, :logo, :promo_image, :path, :pickup, :delivery, :icon, :icon_font, :producer_icon_font, :category, :producers, :hubs - has_many :distributed_taxons, key: :taxons, serializer: Api::IdSerializer - has_many :supplied_taxons, serializer: Api::IdSerializer + attributes :taxons, :supplied_taxons has_one :address, serializer: Api::AddressSerializer + def taxons + options[:distributed_taxons][object.id] + end + + def supplied_taxons + options[:supplied_taxons][object.id] + end + def pickup services = options[:shipping_method_services][object.id] services ? services[:pickup] : false From dd761719eea337c807645ae32a3f7909a81f2e54 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 22 May 2015 11:27:47 +1000 Subject: [PATCH 122/151] Fix undefined Api::IdSerializer error --- app/serializers/api/enterprise_serializer.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index d15ee7860a..6f46d62eda 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -1,4 +1,7 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer + # We reference this here because otherwise the serializer complains about its absence + Api::IdSerializer + def serializable_hash cached_serializer_hash.merge uncached_serializer_hash end @@ -40,6 +43,7 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer has_one :address, serializer: Api::AddressSerializer + def taxons options[:distributed_taxons][object.id] end From 31b726613d5116dc2a1f615aa0f16f55d6f58559 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 22 May 2015 12:17:09 +1000 Subject: [PATCH 123/151] Avoid loading enterprise injection data when it's not be needed due to caching --- app/helpers/injection_helper.rb | 14 ++++------ app/serializers/api/enterprise_serializer.rb | 16 +++++------ .../enterprise_injection_data.rb | 27 +++++++++++++++++++ 3 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 lib/open_food_network/enterprise_injection_data.rb diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index c2e4e6c91a..05057c136b 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -1,6 +1,8 @@ +require 'open_food_network/enterprise_injection_data' + module InjectionHelper def inject_enterprises - inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, enterprise_injection_data + inject_json_ams "enterprises", Enterprise.activated.includes(:address).all, Api::EnterpriseSerializer, enterprise_injection_data end def inject_group_enterprises @@ -66,14 +68,8 @@ module InjectionHelper private def enterprise_injection_data - @active_distributors ||= Enterprise.distributors_with_active_order_cycles - @earliest_closing_times ||= OrderCycle.earliest_closing_times - @shipping_method_services ||= Spree::ShippingMethod.services - @relatives ||= EnterpriseRelationship.relatives - @supplied_taxons ||= Spree::Taxon.supplied_taxons - @distributed_taxons ||= Spree::Taxon.distributed_taxons - - {active_distributors: @active_distributors, earliest_closing_times: @earliest_closing_times, shipping_method_services: @shipping_method_services, relatives: @relatives, supplied_taxons: @supplied_taxons, distributed_taxons: @distributed_taxons} + @enterprise_injection_data ||= OpenFoodNetwork::EnterpriseInjectionData.new + {data: @enterprise_injection_data} end end diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 6f46d62eda..4f38c12964 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -21,11 +21,11 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer attributes :orders_close_at, :active def orders_close_at - options[:earliest_closing_times][object.id] + options[:data].earliest_closing_times[object.id] end def active - options[:active_distributors].andand.include? object + options[:data].active_distributors.andand.include? object end end @@ -45,20 +45,20 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer def taxons - options[:distributed_taxons][object.id] + options[:data].distributed_taxons[object.id] end def supplied_taxons - options[:supplied_taxons][object.id] + options[:data].supplied_taxons[object.id] end def pickup - services = options[:shipping_method_services][object.id] + services = options[:data].shipping_method_services[object.id] services ? services[:pickup] : false end def delivery - services = options[:shipping_method_services][object.id] + services = options[:data].shipping_method_services[object.id] services ? services[:delivery] : false end @@ -83,12 +83,12 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer end def producers - relatives = options[:relatives][object.id] + relatives = options[:data].relatives[object.id] relatives ? relatives[:producers] : [] end def hubs - relatives = options[:relatives][object.id] + relatives = options[:data].relatives[object.id] relatives ? relatives[:distributors] : [] end diff --git a/lib/open_food_network/enterprise_injection_data.rb b/lib/open_food_network/enterprise_injection_data.rb new file mode 100644 index 0000000000..9862418b98 --- /dev/null +++ b/lib/open_food_network/enterprise_injection_data.rb @@ -0,0 +1,27 @@ +module OpenFoodNetwork + class EnterpriseInjectionData + def active_distributors + @active_distributors ||= Enterprise.distributors_with_active_order_cycles + end + + def earliest_closing_times + @earliest_closing_times ||= OrderCycle.earliest_closing_times + end + + def shipping_method_services + @shipping_method_services ||= Spree::ShippingMethod.services + end + + def relatives + @relatives ||= EnterpriseRelationship.relatives + end + + def supplied_taxons + @supplied_taxons ||= Spree::Taxon.supplied_taxons + end + + def distributed_taxons + @distributed_taxons ||= Spree::Taxon.distributed_taxons + end + end +end From e1b4c3b1e4bb75efe713c8b246fdb835004caa36 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 22 May 2015 13:47:24 +1000 Subject: [PATCH 124/151] Add benchmarking test for inject_enterprises --- spec/performance/injection_helper_spec.rb | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 spec/performance/injection_helper_spec.rb diff --git a/spec/performance/injection_helper_spec.rb b/spec/performance/injection_helper_spec.rb new file mode 100644 index 0000000000..4ba22d738f --- /dev/null +++ b/spec/performance/injection_helper_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe InjectionHelper, type: :helper do + let(:oc) { create(:simple_order_cycle) } + let(:relative_supplier) { create(:supplier_enterprise) } + let(:relative_distributor) { create(:distributor_enterprise) } + + before do + 50.times do + e = create(:enterprise) + oc.distributors << e + create(:enterprise_relationship, parent: e, child: relative_supplier) + create(:enterprise_relationship, parent: e, child: relative_distributor) + end + end + + it "is performant in injecting enterprises" do + results = [] + 4.times do |i| + ActiveRecord::Base.connection.query_cache.clear + Rails.cache.clear + result = Benchmark.measure { helper.inject_enterprises } + results << result.total if i > 0 + puts result + end + + puts (results.sum / results.count * 1000).round 0 + end +end From 41bc67e2d8e601b111c8e3820206b79e8cf1e04f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 22 May 2015 14:46:33 +1000 Subject: [PATCH 125/151] Add benchmark for product serialisation --- spec/performance/shop_controller_spec.rb | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 spec/performance/shop_controller_spec.rb diff --git a/spec/performance/shop_controller_spec.rb b/spec/performance/shop_controller_spec.rb new file mode 100644 index 0000000000..14e258d18d --- /dev/null +++ b/spec/performance/shop_controller_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe ShopController, type: :controller do + let(:d) { create(:distributor_enterprise) } + let(:enterprise_fee) { create(:enterprise_fee) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [d], coordinator_fees: [enterprise_fee]) } + + before do + controller.stub(:current_distributor) { d } + controller.stub(:current_order_cycle) { order_cycle } + end + + describe "fetching products" do + let(:exchange) { order_cycle.exchanges.to_enterprises(d).outgoing.first } + let(:image) { File.open(File.expand_path('../../../app/assets/images/logo.jpg', __FILE__)) } + + before do + 11.times do + p = create(:simple_product) + p.set_property 'Organic Certified', 'NASAA 12345' + v1 = create(:variant, product: p) + v2 = create(:variant, product: p) + Spree::Image.create! viewable_id: p.master.id, viewable_type: 'Spree::Variant', attachment: image + + exchange.variants << [v1, v2] + end + end + + it "returns products via json" do + results = [] + 4.times do |i| + ActiveRecord::Base.connection.query_cache.clear + Rails.cache.clear + result = Benchmark.measure do + xhr :get, :products + response.should be_success + end + + results << result.total if i > 0 + puts result + end + + puts (results.sum / results.count * 1000).round 0 + end + end +end From e74390a013b9b614375c98bd63eb847b804923ed Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 May 2015 16:26:08 +1000 Subject: [PATCH 126/151] Remove controller specs for @active_distributors, now set via helper --- spec/controllers/home_controller_spec.rb | 12 ------------ spec/controllers/map_controller_spec.rb | 13 ------------- spec/controllers/producers_controller_spec.rb | 15 --------------- 3 files changed, 40 deletions(-) delete mode 100644 spec/controllers/map_controller_spec.rb delete mode 100644 spec/controllers/producers_controller_spec.rb diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb index bbbff9a7b1..924462741f 100644 --- a/spec/controllers/home_controller_spec.rb +++ b/spec/controllers/home_controller_spec.rb @@ -9,21 +9,9 @@ describe HomeController do Enterprise.stub(:distributors_with_active_order_cycles) { [distributor] } end - it "sets active distributors" do - get :index - assigns[:active_distributors].should == [distributor] - end - # Exclusion from actual rendered view handled in features/consumer/home it "shows JSON for invisible hubs" do get :index response.body.should have_content invisible_distributor.name end - - # This is done inside the json/hubs Serializer - it "gets the next order cycle for each hub" do - OrderCycle.should_receive(:first_closing_for).twice - get :index - end end - diff --git a/spec/controllers/map_controller_spec.rb b/spec/controllers/map_controller_spec.rb deleted file mode 100644 index fab9b7ac22..0000000000 --- a/spec/controllers/map_controller_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'spec_helper' - -describe MapController do - it "loads active distributors" do - active_distributors = double(:distributors) - - Enterprise.stub(:distributors_with_active_order_cycles) { active_distributors } - - get :index - - assigns(:active_distributors).should == active_distributors - end -end diff --git a/spec/controllers/producers_controller_spec.rb b/spec/controllers/producers_controller_spec.rb deleted file mode 100644 index ec3c39036c..0000000000 --- a/spec/controllers/producers_controller_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe ProducersController do - let!(:distributor) { create(:distributor_enterprise) } - - before do - Enterprise.stub(:distributors_with_active_order_cycles) { [distributor] } - Enterprise.stub(:all).and_return([distributor]) - end - - it "sets active distributors" do - get :index - assigns[:active_distributors].should == [distributor] - end -end From 75f1f673ad3d63a6c366947876ce3479b62f050a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 May 2015 16:26:31 +1000 Subject: [PATCH 127/151] Update spec for EnterpriseSerializer --- spec/serializers/enterprise_serializer_spec.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/spec/serializers/enterprise_serializer_spec.rb b/spec/serializers/enterprise_serializer_spec.rb index 1063a042e7..7c467d9c3d 100644 --- a/spec/serializers/enterprise_serializer_spec.rb +++ b/spec/serializers/enterprise_serializer_spec.rb @@ -3,19 +3,18 @@ describe Api::EnterpriseSerializer do let(:enterprise) { create(:distributor_enterprise) } let(:taxon) { create(:taxon) } + let(:data_class) { Struct.new(:earliest_closing_times, :active_distributors, + :distributed_taxons, :supplied_taxons, + :shipping_method_services, :relatives) } + let(:data) { data_class.new({}, [], {}, {}, {}, {producers: [], distributors: []}) } + it "serializes an enterprise" do - serializer = Api::EnterpriseSerializer.new enterprise + serializer = Api::EnterpriseSerializer.new enterprise, data: data serializer.to_json.should match enterprise.name end - it "includes distributed taxons" do - enterprise.stub(:distributed_taxons).and_return [taxon] - serializer = Api::EnterpriseSerializer.new enterprise - serializer.to_json.should match taxon.id.to_s - end - it "will render urls" do - serializer = Api::EnterpriseSerializer.new enterprise + serializer = Api::EnterpriseSerializer.new enterprise, data: data serializer.to_json.should match "map_005-hub.svg" end end From 3ab7df88e68bf024039f1a59ca64283b3daaef95 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 May 2015 16:26:40 +1000 Subject: [PATCH 128/151] Allow serialization of nil enterprise --- app/serializers/api/enterprise_serializer.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 4f38c12964..fc843a5138 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -3,17 +3,18 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer Api::IdSerializer def serializable_hash + cached_serializer_hash.merge uncached_serializer_hash end private def cached_serializer_hash - Api::CachedEnterpriseSerializer.new(object, @options).serializable_hash + Api::CachedEnterpriseSerializer.new(object, @options).serializable_hash || {} end def uncached_serializer_hash - Api::UncachedEnterpriseSerializer.new(object, @options).serializable_hash + Api::UncachedEnterpriseSerializer.new(object, @options).serializable_hash || {} end end @@ -31,7 +32,12 @@ end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer cached - delegate :cache_key, to: :object + #delegate :cache_key, to: :object + + def cache_key + object.andand.cache_key + end + attributes :name, :id, :description, :latitude, :longitude, :long_description, :website, :instagram, :linkedin, :twitter, From ccf1e2951cce17cd4c4ff9f348a1e690976cc466 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 May 2015 10:45:47 +1000 Subject: [PATCH 129/151] Fix intermittent failure in permissions spec --- spec/lib/open_food_network/permissions_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index cf2aa1a217..646a219a5b 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -47,7 +47,7 @@ module OpenFoodNetwork expect(permissions).to receive(:managed_enterprises) { Enterprise.where(id: e1) } expect(permissions).to receive(:related_enterprises_granting).with(:some_permission) { Enterprise.where(id: e3) } expect(permissions).to receive(:related_enterprises_granted).with(:some_permission) { Enterprise.where(id: e4) } - expect(permissions.send(:managed_and_related_enterprises_with, :some_permission)).to eq [e1, e3, e4] + expect(permissions.send(:managed_and_related_enterprises_with, :some_permission)).to match_array [e1, e3, e4] end end end From 97e49c2bdb4c3173ec357ce8a498bb54e7235123 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 May 2015 10:46:08 +1000 Subject: [PATCH 130/151] Replace 'array.sort.should == expected.sort' pattern with match_array --- .../spree/admin/orders_controller_spec.rb | 4 ++-- .../spree/admin/reports_controller_spec.rb | 12 +++++----- .../spree/admin/variants_controller_spec.rb | 2 +- spec/features/admin/enterprise_groups_spec.rb | 2 +- .../admin/enterprise_relationships_spec.rb | 2 +- spec/features/admin/order_cycles_spec.rb | 20 ++++++++-------- spec/features/admin/products_spec.rb | 4 ++-- spec/features/admin/shipping_methods_spec.rb | 2 +- .../customers_report_spec.rb | 18 +++++++------- .../order_cycle_form_applicator_spec.rb | 16 ++++++------- .../lib/open_food_network/permissions_spec.rb | 2 +- .../products_and_inventory_report_spec.rb | 22 ++++++++--------- spec/models/enterprise_fee_spec.rb | 4 ++-- spec/models/enterprise_relationship_spec.rb | 8 +++---- spec/models/enterprise_spec.rb | 22 ++++++++--------- spec/models/exchange_spec.rb | 6 ++--- spec/models/order_cycle_spec.rb | 24 +++++++++---------- spec/models/spree/line_item_spec.rb | 2 +- spec/models/spree/payment_spec.rb | 4 ++-- spec/models/spree/product_spec.rb | 2 +- spec/models/spree/shipping_method_spec.rb | 2 +- spec/models/spree/variant_spec.rb | 2 +- spec/models/variant_override_spec.rb | 2 +- 23 files changed, 92 insertions(+), 92 deletions(-) diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index ac6a5532b2..dae1edcf29 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -143,7 +143,7 @@ describe Spree::Admin::OrdersController do end it "only displays line items from orders for which my enterprise is the order_cycle coorinator" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.sort.should == [line_item1.id, line_item2.id, line_item3.id].sort + json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.should match_array [line_item1.id, line_item2.id, line_item3.id] end end @@ -159,7 +159,7 @@ describe Spree::Admin::OrdersController do end it "only displays line items from orders for which my enterprise is a distributor" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.sort.should == [line_item1.id, line_item2.id].sort + json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.should match_array [line_item1.id, line_item2.id] end end end diff --git a/spec/controllers/spree/admin/reports_controller_spec.rb b/spec/controllers/spree/admin/reports_controller_spec.rb index 58d406519b..e930c1e339 100644 --- a/spec/controllers/spree/admin/reports_controller_spec.rb +++ b/spec/controllers/spree/admin/reports_controller_spec.rb @@ -170,17 +170,17 @@ describe Spree::Admin::ReportsController do it "should build distributors for the current user" do spree_get :products_and_inventory - assigns(:distributors).sort.should == [c1, c2, d1, d2, d3].sort + assigns(:distributors).should match_array [c1, c2, d1, d2, d3] end it "builds suppliers for the current user" do spree_get :products_and_inventory - assigns(:suppliers).sort.should == [s1, s2, s3].sort + assigns(:suppliers).should match_array [s1, s2, s3] end it "builds order cycles for the current user" do spree_get :products_and_inventory - assigns(:order_cycles).sort.should == [ocB, ocA].sort + assigns(:order_cycles).should match_array [ocB, ocA] end it "assigns report types" do @@ -211,17 +211,17 @@ describe Spree::Admin::ReportsController do it "should build distributors for the current user" do spree_get :customers - assigns(:distributors).sort.should == [c1, c2, d1, d2, d3].sort + assigns(:distributors).should match_array [c1, c2, d1, d2, d3] end it "builds suppliers for the current user" do spree_get :customers - assigns(:suppliers).sort.should == [s1, s2, s3].sort + assigns(:suppliers).should match_array [s1, s2, s3] end it "builds order cycles for the current user" do spree_get :customers - assigns(:order_cycles).sort.should == [ocB, ocA].sort + assigns(:order_cycles).should match_array [ocB, ocA] end it "assigns report types" do diff --git a/spec/controllers/spree/admin/variants_controller_spec.rb b/spec/controllers/spree/admin/variants_controller_spec.rb index 22972a7301..965ff8d7ce 100644 --- a/spec/controllers/spree/admin/variants_controller_spec.rb +++ b/spec/controllers/spree/admin/variants_controller_spec.rb @@ -30,7 +30,7 @@ module Spree it "does not filter when no distributor or order cycle is specified" do spree_get :search, q: 'Prod' - assigns(:variants).sort.should == [p1.master, p2.master].sort + assigns(:variants).should match_array [p1.master, p2.master] end end end diff --git a/spec/features/admin/enterprise_groups_spec.rb b/spec/features/admin/enterprise_groups_spec.rb index 59712f3bfe..2adb96c0a5 100644 --- a/spec/features/admin/enterprise_groups_spec.rb +++ b/spec/features/admin/enterprise_groups_spec.rb @@ -50,7 +50,7 @@ feature %q{ eg.name.should == 'EGEGEG' eg.description.should == 'This is a description' eg.on_front_page.should be_true - eg.enterprises.sort.should == [e1, e2].sort + eg.enterprises.should match_array [e1, e2] end scenario "editing an enterprise group" do diff --git a/spec/features/admin/enterprise_relationships_spec.rb b/spec/features/admin/enterprise_relationships_spec.rb index 3a2d9692de..ef0f1e5537 100644 --- a/spec/features/admin/enterprise_relationships_spec.rb +++ b/spec/features/admin/enterprise_relationships_spec.rb @@ -50,7 +50,7 @@ feature %q{ page.should have_relationship e1, e2, ['to add to order cycle', 'to override variant details', 'to edit profile'] er = EnterpriseRelationship.where(parent_id: e1, child_id: e2).first er.should be_present - er.permissions.map(&:name).sort.should == ['add_to_order_cycle', 'edit_profile', 'create_variant_overrides'].sort + er.permissions.map(&:name).should match_array ['add_to_order_cycle', 'edit_profile', 'create_variant_overrides'] end diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index cb96799fbb..eed6551bfa 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -354,7 +354,7 @@ feature %q{ page.should have_selector 'td.distributors', text: 'My distributor' # And my coordinator fees should have been configured - OrderCycle.last.coordinator_fee_ids.sort.should == [coordinator_fee1.id, coordinator_fee2.id].sort + OrderCycle.last.coordinator_fee_ids.should match_array [coordinator_fee1.id, coordinator_fee2.id] # And my supplier fees should have been configured OrderCycle.last.exchanges.incoming.last.enterprise_fee_ids.should == [supplier_fee2.id] @@ -364,7 +364,7 @@ feature %q{ # And it should have some variants selected selected_initial_variants = initial_variants.take initial_variants.size - 1 - OrderCycle.last.variants.map(&:id).sort.should == (selected_initial_variants.map(&:id) + [v1.id, v2.id]).sort + OrderCycle.last.variants.map(&:id).should match_array (selected_initial_variants.map(&:id) + [v1.id, v2.id]) # And the collection details should have been updated OrderCycle.last.exchanges.where(pickup_time: 'New time 0', pickup_instructions: 'New instructions 0').should be_present @@ -568,9 +568,9 @@ feature %q{ flash_message.should == "Your order cycle has been created." order_cycle = OrderCycle.find_by_name('My order cycle') - order_cycle.suppliers.sort.should == [supplier_managed, supplier_permitted].sort + order_cycle.suppliers.should match_array [supplier_managed, supplier_permitted] order_cycle.coordinator.should == distributor_managed - order_cycle.distributors.sort.should == [distributor_managed, distributor_permitted].sort + order_cycle.distributors.should match_array [distributor_managed, distributor_permitted] end scenario "editing an order cycle we can see (and for now, edit) all exchanges in the order cycle" do @@ -592,9 +592,9 @@ feature %q{ page.should have_content "Your order cycle has been updated." oc.reload - oc.suppliers.sort.should == [supplier_managed, supplier_permitted, supplier_unmanaged].sort + oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed - oc.distributors.sort.should == [distributor_managed, distributor_permitted, distributor_unmanaged].sort + oc.distributors.should match_array [distributor_managed, distributor_permitted, distributor_unmanaged] end scenario "editing an order cycle" do @@ -683,9 +683,9 @@ feature %q{ page.should have_content "Your order cycle has been updated." oc.reload - oc.suppliers.sort.should == [supplier_managed, supplier_permitted, supplier_unmanaged].sort + oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed - oc.distributors.sort.should == [distributor_managed, distributor_permitted, distributor_unmanaged].sort + oc.distributors.should match_array [distributor_managed, distributor_permitted, distributor_unmanaged] end end @@ -736,9 +736,9 @@ feature %q{ page.should have_content "Your order cycle has been updated." oc.reload - oc.suppliers.sort.should == [supplier_managed, supplier_permitted, supplier_unmanaged].sort + oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed - oc.distributors.sort.should == [my_distributor, distributor_managed, distributor_permitted, distributor_unmanaged].sort + oc.distributors.should match_array [my_distributor, distributor_managed, distributor_permitted, distributor_unmanaged] end end end diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index 740de12045..95569799b4 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -68,10 +68,10 @@ feature %q{ click_button 'Update' product.reload - product.distributors.sort.should == [@distributors[0], @distributors[2]].sort + product.distributors.should match_array [@distributors[0], @distributors[2]] - product.product_distributions.map { |pd| pd.enterprise_fee }.sort.should == [@enterprise_fees[0], @enterprise_fees[2]].sort + product.product_distributions.map { |pd| pd.enterprise_fee }.should match_array [@enterprise_fees[0], @enterprise_fees[2]] end scenario "making a product into a group buy product" do diff --git a/spec/features/admin/shipping_methods_spec.rb b/spec/features/admin/shipping_methods_spec.rb index 2aeeb1db1d..2857fa6502 100644 --- a/spec/features/admin/shipping_methods_spec.rb +++ b/spec/features/admin/shipping_methods_spec.rb @@ -36,7 +36,7 @@ feature 'shipping methods' do sm = Spree::ShippingMethod.last sm.name.should == 'Carrier Pidgeon' - sm.distributors.sort.should == [d1, d2].sort + sm.distributors.should match_array [d1, d2] end it "at checkout, user can only see shipping methods for their current distributor (checkout spec)" diff --git a/spec/lib/open_food_network/customers_report_spec.rb b/spec/lib/open_food_network/customers_report_spec.rb index c655fa626e..289a32929e 100644 --- a/spec/lib/open_food_network/customers_report_spec.rb +++ b/spec/lib/open_food_network/customers_report_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' module OpenFoodNetwork describe CustomersReport do context "as a site admin" do - let(:user) do + let(:user) do user = create(:user) user.spree_roles << Spree::Role.find_or_create_by_name!("admin") user @@ -44,14 +44,14 @@ module OpenFoodNetwork it "builds a table from a list of variants" do a = create(:address) d = create(:distributor_enterprise) - o = create(:order, distributor: d, bill_address: a) + o = create(:order, distributor: d, bill_address: a) o.shipping_method = create(:shipping_method) subject.stub(:orders).and_return [o] subject.table.should == [[ - a.firstname, a.lastname, - [a.address1, a.address2, a.city].join(" "), - o.email, a.phone, d.name, + a.firstname, a.lastname, + [a.address1, a.address2, a.city].join(" "), + o.email, a.phone, d.name, [d.address.address1, d.address.address2, d.address.city].join(" "), o.shipping_method.name ]] @@ -74,7 +74,7 @@ module OpenFoodNetwork end context "as an enterprise user" do - let(:user) do + let(:user) do user = create(:user) user.spree_roles = [] user.save! @@ -131,7 +131,7 @@ module OpenFoodNetwork order2.line_items << create(:line_item, product: product2) subject.stub(:params).and_return(supplier_id: supplier.id) - subject.filter(orders).sort.should == [order1] + subject.filter(orders).should == [order1] end it "filters to a specific distributor" do @@ -141,7 +141,7 @@ module OpenFoodNetwork order2 = create(:order, distributor: d2) subject.stub(:params).and_return(distributor_id: d1.id) - subject.filter(orders).sort.should == [order1] + subject.filter(orders).should == [order1] end it "filters to a specific cycle" do @@ -151,7 +151,7 @@ module OpenFoodNetwork order2 = create(:order, order_cycle: oc2) subject.stub(:params).and_return(order_cycle_id: oc1.id) - subject.filter(orders).sort.should == [order1] + subject.filter(orders).should == [order1] end end end diff --git a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb index 6a03964039..5314c4376c 100644 --- a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb +++ b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb @@ -300,8 +300,8 @@ module OpenFoodNetwork expect(exchange.sender).to eq sender expect(exchange.receiver).to eq receiver expect(exchange.incoming).to eq incoming - expect(exchange.variants.sort).to eq [variant1, variant2].sort - expect(exchange.enterprise_fees.sort).to eq [enterprise_fee1, enterprise_fee2].sort + expect(exchange.variants).to match_array [variant1, variant2] + expect(exchange.enterprise_fees).to match_array [enterprise_fee1, enterprise_fee2] applicator.send(:touched_exchanges).should == [exchange] end @@ -345,8 +345,8 @@ module OpenFoodNetwork it "updates the variants, enterprise fees and pickup information of the exchange" do exchange.reload - expect(exchange.variants.sort).to eq [variant1, variant3].sort - expect(exchange.enterprise_fees.sort).to eq [enterprise_fee2, enterprise_fee3] + expect(exchange.variants).to match_array [variant1, variant3] + expect(exchange.enterprise_fees).to match_array [enterprise_fee2, enterprise_fee3] expect(exchange.pickup_time).to eq 'New Pickup Time' expect(exchange.pickup_instructions).to eq 'New Pickup Instructions' expect(applicator.send(:touched_exchanges)).to eq [exchange] @@ -364,8 +364,8 @@ module OpenFoodNetwork it "updates the variants, enterprise fees and pickup information of the exchange" do exchange.reload - expect(exchange.variants.sort).to eq [variant1, variant3].sort - expect(exchange.enterprise_fees.sort).to eq [enterprise_fee2, enterprise_fee3] + expect(exchange.variants).to match_array [variant1, variant3] + expect(exchange.enterprise_fees).to match_array [enterprise_fee2, enterprise_fee3] expect(exchange.pickup_time).to eq 'New Pickup Time' expect(exchange.pickup_instructions).to eq 'New Pickup Instructions' expect(applicator.send(:touched_exchanges)).to eq [exchange] @@ -383,8 +383,8 @@ module OpenFoodNetwork it "updates the variants in the exchange, but not the fees or pickup information" do exchange.reload - expect(exchange.variants.sort).to eq [variant1, variant3].sort - expect(exchange.enterprise_fees.sort).to eq [enterprise_fee1, enterprise_fee2] + expect(exchange.variants).to match_array [variant1, variant3] + expect(exchange.enterprise_fees).to match_array [enterprise_fee1, enterprise_fee2] expect(exchange.pickup_time).to_not eq 'New Pickup Time' expect(exchange.pickup_instructions).to_not eq 'New Pickup Instructions' expect(applicator.send(:touched_exchanges)).to eq [exchange] diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 646a219a5b..4224a00d89 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -98,7 +98,7 @@ module OpenFoodNetwork {1 => [e1.id], 2 => [e1.id, e2.id]} end - permissions.variant_override_producers.sort.should == [e1, e2].sort + permissions.variant_override_producers.should match_array [e1, e2] end end diff --git a/spec/lib/open_food_network/products_and_inventory_report_spec.rb b/spec/lib/open_food_network/products_and_inventory_report_spec.rb index 2e23919fe6..4ae93e7e64 100644 --- a/spec/lib/open_food_network/products_and_inventory_report_spec.rb +++ b/spec/lib/open_food_network/products_and_inventory_report_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' module OpenFoodNetwork describe ProductsAndInventoryReport do context "As a site admin" do - let(:user) do + let(:user) do user = create(:user) user.spree_roles << Spree::Role.find_or_create_by_name!("admin") user @@ -14,7 +14,7 @@ module OpenFoodNetwork it "Should return headers" do subject.header.should == [ - "Supplier", + "Supplier", "Producer Suburb", "Product", "Product Properties", @@ -63,7 +63,7 @@ module OpenFoodNetwork context "As an enterprise user" do let(:supplier) { create(:supplier_enterprise) } - let(:enterprise_user) do + let(:enterprise_user) do user = create(:user) user.enterprise_roles.create(enterprise: supplier) user.spree_roles = [] @@ -79,7 +79,7 @@ module OpenFoodNetwork variant_1 = create(:variant, product: product1) variant_2 = create(:variant, product: product1) - subject.child_variants.sort.should == [variant_1, variant_2].sort + subject.child_variants.should match_array [variant_1, variant_2] end it "should only return variants managed by the user" do @@ -87,7 +87,7 @@ module OpenFoodNetwork product2 = create(:simple_product, supplier: supplier) variant_1 = create(:variant, product: product1) variant_2 = create(:variant, product: product2) - + subject.child_variants.should == [variant_2] end end @@ -96,15 +96,15 @@ module OpenFoodNetwork it "should only return variants managed by the user" do product1 = create(:simple_product, supplier: create(:supplier_enterprise)) product2 = create(:simple_product, supplier: supplier) - + subject.master_variants.should == [product2.master] end it "doesn't return master variants with siblings" do product = create(:simple_product, supplier: supplier) - create(:variant, product: product) - - subject.master_variants.should be_empty + create(:variant, product: product) + + subject.master_variants.should be_empty end end @@ -113,13 +113,13 @@ module OpenFoodNetwork it "should return unfiltered variants sans-params" do product1 = create(:simple_product, supplier: supplier) product2 = create(:simple_product, supplier: supplier) - subject.filter(Spree::Variant.scoped).sort.should == [product1.master, product2.master].sort + subject.filter(Spree::Variant.scoped).should match_array [product1.master, product2.master] end it "should filter deleted products" do product1 = create(:simple_product, supplier: supplier) product2 = create(:simple_product, supplier: supplier) product2.delete - subject.filter(Spree::Variant.scoped).sort.should == [product1.master].sort + subject.filter(Spree::Variant.scoped).should match_array [product1.master] end describe "based on report type" do it "returns only variants on hand" do diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb index fc72f383d2..f0f9df1f0b 100644 --- a/spec/models/enterprise_fee_spec.rb +++ b/spec/models/enterprise_fee_spec.rb @@ -43,7 +43,7 @@ describe EnterpriseFee do ef3 = create(:enterprise_fee, calculator: Spree::Calculator::PerItem.new) ef4 = create(:enterprise_fee, calculator: Spree::Calculator::PriceSack.new) - EnterpriseFee.per_item.sort.should == [ef1, ef2, ef3, ef4].sort + EnterpriseFee.per_item.should match_array [ef1, ef2, ef3, ef4] end end @@ -52,7 +52,7 @@ describe EnterpriseFee do ef1 = create(:enterprise_fee, calculator: Spree::Calculator::FlatRate.new) ef2 = create(:enterprise_fee, calculator: Spree::Calculator::FlexiRate.new) - EnterpriseFee.per_order.sort.should == [ef1, ef2].sort + EnterpriseFee.per_order.should match_array [ef1, ef2] end it "does not return fees with any other calculator" do diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb index e87c204037..cbbdd00504 100644 --- a/spec/models/enterprise_relationship_spec.rb +++ b/spec/models/enterprise_relationship_spec.rb @@ -34,7 +34,7 @@ describe EnterpriseRelationship do it "creates permissions with a list" do er = EnterpriseRelationship.create! parent: e1, child: e2, permissions_list: ['one', 'two'] er.reload - er.permissions.map(&:name).sort.should == ['one', 'two'].sort + er.permissions.map(&:name).should match_array ['one', 'two'] end it "does nothing when the list is nil" do @@ -50,11 +50,11 @@ describe EnterpriseRelationship do let!(:er3) { create(:enterprise_relationship, parent: e1, child: e3) } it "finds relationships that grant permissions to some enterprises" do - EnterpriseRelationship.permitting([e1, e2]).sort.should == [er1, er2].sort + EnterpriseRelationship.permitting([e1, e2]).should match_array [er1, er2] end it "finds relationships that are granted by particular enterprises" do - EnterpriseRelationship.permitted_by([e1, e2]).sort.should == [er1, er3].sort + EnterpriseRelationship.permitted_by([e1, e2]).should match_array [er1, er3] end end @@ -66,7 +66,7 @@ describe EnterpriseRelationship do er3 = create(:enterprise_relationship, parent: e3, child: e1, permissions_list: ['three', 'four']) - EnterpriseRelationship.with_permission('two').sort.should == [er1, er2].sort + EnterpriseRelationship.with_permission('two').should match_array [er1, er2] end end end diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index df1b58aca6..ed4669c0c2 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -118,7 +118,7 @@ describe Enterprise do let!(:er2) { create(:enterprise_relationship, parent_id: e.id, child_id: c.id) } it "finds relatives" do - e.relatives.sort.should == [p, c].sort + e.relatives.should match_array [p, c] end it "scopes relatives to visible distributors" do @@ -422,7 +422,7 @@ describe Enterprise do create(:product, :distributors => [d1], :on_hand => 5) create(:product, :distributors => [d3], :on_hand => 0) - Enterprise.with_distributed_active_products_on_hand.sort.should == [d1, d2] + Enterprise.with_distributed_active_products_on_hand.should match_array [d1, d2] end it "returns distributors with available products in stock" do @@ -438,7 +438,7 @@ describe Enterprise do create(:product, :distributors => [d4], :on_hand => 0) create(:product, :distributors => [d5]).delete - Enterprise.with_distributed_active_products_on_hand.sort.should == [d1, d2] + Enterprise.with_distributed_active_products_on_hand.should match_array [d1, d2] Enterprise.with_distributed_active_products_on_hand.distinct_count.should == 2 end end @@ -458,7 +458,7 @@ describe Enterprise do create(:product, :supplier => d4, :on_hand => 0) create(:product, :supplier => d5).delete - Enterprise.with_supplied_active_products_on_hand.sort.should == [d1, d2] + Enterprise.with_supplied_active_products_on_hand.should match_array [d1, d2] Enterprise.with_supplied_active_products_on_hand.distinct_count.should == 2 end end @@ -485,7 +485,7 @@ describe Enterprise do p1 = create(:simple_product, supplier: s1) p2 = create(:simple_product, supplier: s2) - Enterprise.supplying_variant_in([p1.master, p2.master]).sort.should == [s1, s2].sort + Enterprise.supplying_variant_in([p1.master, p2.master]).should match_array [s1, s2] end it "does not return duplicates" do @@ -625,9 +625,9 @@ describe Enterprise do er = EnterpriseRelationship.where(parent_id: opts[:from], child_id: opts[:to]).last er.should_not be_nil if opts[:with] == :all_permissions - er.permissions.map(&:name).sort.should == ['add_to_order_cycle', 'manage_products', 'edit_profile', 'create_variant_overrides'].sort + er.permissions.map(&:name).should match_array ['add_to_order_cycle', 'manage_products', 'edit_profile', 'create_variant_overrides'] elsif opts.key? :with - er.permissions.map(&:name).sort.should == opts[:with].map(&:to_s).sort + er.permissions.map(&:name).should match_array opts[:with].map(&:to_s) end end end @@ -674,7 +674,7 @@ describe Enterprise do d = create(:distributor_enterprise) p = create(:product, distributors: [d]) v = create(:variant, product: p) - d.distributed_variants.sort.should == [p.master, v].sort + d.distributed_variants.should match_array [p.master, v] end it "finds variants distributed by order cycle" do @@ -696,7 +696,7 @@ describe Enterprise do d = create(:distributor_enterprise) p = create(:product, distributors: [d]) v = create(:variant, product: p) - d.product_distribution_variants.sort.should == [p.master, v].sort + d.product_distribution_variants.should match_array [p.master, v] end it "does not find variants distributed by order cycle" do @@ -752,12 +752,12 @@ describe Enterprise do it "gets all taxons of all distributed products" do Spree::Product.stub(:in_distributor).and_return [product1, product2] - distributor.distributed_taxons.sort.should == [taxon1, taxon2].sort + distributor.distributed_taxons.should match_array [taxon1, taxon2] end it "gets all taxons of all supplied products" do Spree::Product.stub(:in_supplier).and_return [product1, product2] - supplier.supplied_taxons.sort.should == [taxon1, taxon2].sort + supplier.supplied_taxons.should match_array [taxon1, taxon2] end end diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index 82f360aa16..0df7aeafab 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -170,17 +170,17 @@ describe Exchange do it "finds exchanges coming from any of a number of enterprises" do Exchange.from_enterprises([coordinator]).should == [outgoing_exchange] - Exchange.from_enterprises([supplier, coordinator]).sort.should == [incoming_exchange, outgoing_exchange].sort + Exchange.from_enterprises([supplier, coordinator]).should match_array [incoming_exchange, outgoing_exchange] end it "finds exchanges going to any of a number of enterprises" do Exchange.to_enterprises([coordinator]).should == [incoming_exchange] - Exchange.to_enterprises([coordinator, distributor]).sort.should == [incoming_exchange, outgoing_exchange].sort + Exchange.to_enterprises([coordinator, distributor]).should match_array [incoming_exchange, outgoing_exchange] end it "finds exchanges involving any of a number of enterprises" do Exchange.involving([supplier]).should == [incoming_exchange] - Exchange.involving([coordinator]).sort.should == [incoming_exchange, outgoing_exchange].sort + Exchange.involving([coordinator]).should match_array [incoming_exchange, outgoing_exchange] Exchange.involving([distributor]).should == [outgoing_exchange] end end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 817d2e8e93..46cefd0600 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -37,7 +37,7 @@ describe OrderCycle do oc_undated = create(:simple_order_cycle, orders_open_at: nil, orders_close_at: nil) OrderCycle.active.should == [oc_active] - OrderCycle.inactive.sort.should == [oc_not_yet_open, oc_already_closed].sort + OrderCycle.inactive.should match_array [oc_not_yet_open, oc_already_closed] OrderCycle.upcoming.should == [oc_not_yet_open] OrderCycle.closed.should == [oc_already_closed] OrderCycle.undated.should == [oc_undated] @@ -153,7 +153,7 @@ describe OrderCycle do e2 = create(:exchange, incoming: true, order_cycle: oc, receiver: oc.coordinator, sender: create(:enterprise)) - oc.suppliers.sort.should == [e1.sender, e2.sender].sort + oc.suppliers.should match_array [e1.sender, e2.sender] end it "reports its distributors" do @@ -164,7 +164,7 @@ describe OrderCycle do e2 = create(:exchange, incoming: false, order_cycle: oc, sender: oc.coordinator, receiver: create(:enterprise)) - oc.distributors.sort.should == [e1.receiver, e2.receiver].sort + oc.distributors.should match_array [e1.receiver, e2.receiver] end it "checks for existance of distributors" do @@ -215,11 +215,11 @@ describe OrderCycle do end it "reports on the variants exchanged" do - @oc.variants.sort.should == [@p0.master, @p1.master, @p2.master, @p2_v].sort + @oc.variants.should match_array [@p0.master, @p1.master, @p2.master, @p2_v] end it "reports on the variants distributed" do - @oc.distributed_variants.sort.should == [@p1.master, @p2.master, @p2_v].sort + @oc.distributed_variants.should match_array [@p1.master, @p2.master, @p2_v] end it "reports on the variants distributed by a particular distributor" do @@ -231,7 +231,7 @@ describe OrderCycle do end it "reports on the products exchanged" do - @oc.products.sort.should == [@p0, @p1, @p2] + @oc.products.should match_array [@p0, @p1, @p2] end end @@ -313,7 +313,7 @@ describe OrderCycle do @oc.pickup_time_for(@d2).should == '2-8pm Friday' end end - + describe "finding pickup instructions for a distributor" do it "returns the pickup instructions" do @oc.pickup_instructions_for(@d1).should == "Come get it!" @@ -375,7 +375,7 @@ describe OrderCycle do occ.coordinator_fee_ids.should_not be_empty occ.coordinator_fee_ids.should == oc.coordinator_fee_ids - + # to_h gives us a unique hash for each exchange # check that the clone has no additional exchanges occ.exchanges.map(&:to_h).all? do |ex| @@ -402,7 +402,7 @@ describe OrderCycle do describe "finding order cycles opening in the future" do it "should give the soonest opening order cycle for a distributor" do distributor = create(:distributor_enterprise) - oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 10.days.from_now, orders_close_at: 11.days.from_now) + oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 10.days.from_now, orders_close_at: 11.days.from_now) OrderCycle.first_opening_for(distributor).should == oc end @@ -411,12 +411,12 @@ describe OrderCycle do OrderCycle.first_opening_for(distributor).should == nil end end - + describe "finding open order cycles" do it "should give the soonest closing order cycle for a distributor" do distributor = create(:distributor_enterprise) - oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 1.days.ago, orders_close_at: 11.days.from_now) - oc2 = create(:simple_order_cycle, name: 'oc 2', distributors: [distributor], orders_open_at: 2.days.ago, orders_close_at: 12.days.from_now) + oc = create(:simple_order_cycle, name: 'oc 1', distributors: [distributor], orders_open_at: 1.days.ago, orders_close_at: 11.days.from_now) + oc2 = create(:simple_order_cycle, name: 'oc 2', distributors: [distributor], orders_open_at: 2.days.ago, orders_close_at: 12.days.from_now) OrderCycle.first_closing_for(distributor).should == oc end end diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index 6166732b76..4058ba30e1 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -22,7 +22,7 @@ module Spree it "finds line items for products supplied by one of a number of enterprises" do LineItem.supplied_by_any([s1]).should == [li1] LineItem.supplied_by_any([s2]).should == [li2] - LineItem.supplied_by_any([s1, s2]).sort.should == [li1, li2].sort + LineItem.supplied_by_any([s1, s2]).should match_array [li1, li2] end end diff --git a/spec/models/spree/payment_spec.rb b/spec/models/spree/payment_spec.rb index 8a781e7700..0877959cbb 100644 --- a/spec/models/spree/payment_spec.rb +++ b/spec/models/spree/payment_spec.rb @@ -7,7 +7,7 @@ module Spree let(:payment) { create(:payment, source: create(:credit_card)) } it "can capture and void" do - payment.actions.sort.should == %w(capture void).sort + payment.actions.should match_array %w(capture void) end describe "when a payment has been taken" do @@ -17,7 +17,7 @@ module Spree end it "can void and credit" do - payment.actions.sort.should == %w(void credit).sort + payment.actions.should match_array %w(void credit) end end end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 275d8b1970..287f60b400 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -545,7 +545,7 @@ module Spree ot3 = create(:option_type, name: 'unit_items', presentation: 'Items') ot4 = create(:option_type, name: 'foo_unit_bar', presentation: 'Foo') - Spree::Product.all_variant_unit_option_types.sort.should == [ot1, ot2, ot3].sort + Spree::Product.all_variant_unit_option_types.should match_array [ot1, ot2, ot3] end end diff --git a/spec/models/spree/shipping_method_spec.rb b/spec/models/spree/shipping_method_spec.rb index d6b821e890..baea2ac56a 100644 --- a/spec/models/spree/shipping_method_spec.rb +++ b/spec/models/spree/shipping_method_spec.rb @@ -15,7 +15,7 @@ module Spree sm.distributors << d1 sm.distributors << d2 - sm.reload.distributors.sort.should == [d1, d2].sort + sm.reload.distributors.should match_array [d1, d2] end it "finds shipping methods for a particular distributor" do diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 5a82f04ba7..866f17ff1d 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -25,7 +25,7 @@ module Spree end it "returns variants in stock or on demand, but not those that are neither" do - Variant.where(is_master: false).in_stock.sort.should == [@v_in_stock, @v_on_demand].sort + Variant.where(is_master: false).in_stock.should match_array [@v_in_stock, @v_on_demand] end end diff --git a/spec/models/variant_override_spec.rb b/spec/models/variant_override_spec.rb index ee06bd20ec..3a6bd9b1df 100644 --- a/spec/models/variant_override_spec.rb +++ b/spec/models/variant_override_spec.rb @@ -12,7 +12,7 @@ describe VariantOverride do let!(:vo2) { create(:variant_override, hub: hub2, variant: v) } it "finds variant overrides for a set of hubs" do - VariantOverride.for_hubs([hub1, hub2]).sort.should == [vo1, vo2].sort + VariantOverride.for_hubs([hub1, hub2]).should match_array [vo1, vo2] end end From 7f43dbf9bb23b237de19e6cd9b564cca4b394f37 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 May 2015 10:58:12 +1000 Subject: [PATCH 131/151] Fix further intermittent failures in permissions spec --- spec/lib/open_food_network/permissions_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 4224a00d89..1b6e23eeb0 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -13,7 +13,7 @@ module OpenFoodNetwork before { allow(user).to receive(:admin?) { true } } it "returns all enterprises" do - expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to eq [e1, e2] + expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to match_array [e1, e2] end end @@ -24,7 +24,7 @@ module OpenFoodNetwork it "returns only my managed enterprises any that have granting them P-OC" do expect(permissions).to receive(:managed_enterprises) { Enterprise.where(id: e1) } expect(permissions).to receive(:related_enterprises_granting).with(:some_permission) { Enterprise.where(id: e3) } - expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to eq [e1, e3] + expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to match_array [e1, e3] end end end @@ -34,7 +34,7 @@ module OpenFoodNetwork before { allow(user).to receive(:admin?) { true } } it "returns all enterprises" do - expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to eq [e1, e2] + expect(permissions.send(:managed_and_related_enterprises_granting, :some_permission)).to match_array [e1, e2] end end From 19448a182ecade379bc1efaba59181f7e766f851 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 27 May 2015 11:54:13 +1000 Subject: [PATCH 132/151] Add permalink field to enterprise groups --- app/models/enterprise_group.rb | 1 + .../20150527004427_add_permalink_to_groups.rb | 26 +++++++++++++++++++ db/schema.rb | 6 +++-- spec/factories.rb | 1 + 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20150527004427_add_permalink_to_groups.rb diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 9d510d8f2b..aae2af4b17 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -15,6 +15,7 @@ class EnterpriseGroup < ActiveRecord::Base validates :name, presence: true validates :description, presence: true + validates :permalink, uniqueness: true, presence: true attr_accessible :name, :description, :long_description, :on_front_page, :enterprise_ids attr_accessible :owner_id diff --git a/db/migrate/20150527004427_add_permalink_to_groups.rb b/db/migrate/20150527004427_add_permalink_to_groups.rb new file mode 100644 index 0000000000..ab6886cff4 --- /dev/null +++ b/db/migrate/20150527004427_add_permalink_to_groups.rb @@ -0,0 +1,26 @@ +class AddPermalinkToGroups < ActiveRecord::Migration + def up + add_column :enterprise_groups, :permalink, :string + + EnterpriseGroup.reset_column_information + + EnterpriseGroup.all.each do |group| + counter = 1 + permalink = group.name.parameterize + permalink = "my-group-name" if permalink == "" + while EnterpriseGroup.find_by_permalink(permalink) do + permalink = group.name.parameterize + counter.to_s + counter += 1 + end + + group.update_column :permalink, permalink + end + + change_column :enterprise_groups, :permalink, :string, null: false + add_index :enterprise_groups, :permalink, :unique => true + end + + def down + remove_column :enterprise_groups, :permalink + end +end diff --git a/db/schema.rb b/db/schema.rb index f9f32af7d1..6d9de020f1 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 => 20150424151117) do +ActiveRecord::Schema.define(:version => 20150527004427) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -236,10 +236,12 @@ ActiveRecord::Schema.define(:version => 20150424151117) do t.string "linkedin", :default => "", :null => false t.string "twitter", :default => "", :null => false t.integer "owner_id" + t.string "permalink", :null => false end add_index "enterprise_groups", ["address_id"], :name => "index_enterprise_groups_on_address_id" add_index "enterprise_groups", ["owner_id"], :name => "index_enterprise_groups_on_owner_id" + add_index "enterprise_groups", ["permalink"], :name => "index_enterprise_groups_on_permalink", :unique => true create_table "enterprise_groups_enterprises", :id => false, :force => true do |t| t.integer "enterprise_group_id" @@ -619,9 +621,9 @@ ActiveRecord::Schema.define(:version => 20150424151117) 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" end diff --git a/spec/factories.rb b/spec/factories.rb index cb4d06b35e..f61172dc2a 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -135,6 +135,7 @@ FactoryGirl.define do factory :enterprise_group, :class => EnterpriseGroup do name 'Enterprise group' + sequence(:permalink) { |n| "group#{n}" } description 'this is a group' on_front_page false address { FactoryGirl.build(:address) } From aef128f2c9282793ad55d7b9fa09a8413d06cdfc Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 27 May 2015 12:20:54 +1000 Subject: [PATCH 133/151] permalink editable --- app/models/enterprise_group.rb | 1 + .../admin/enterprise_groups/_form_primary_details.html.haml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index aae2af4b17..1524a80b53 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -19,6 +19,7 @@ class EnterpriseGroup < ActiveRecord::Base attr_accessible :name, :description, :long_description, :on_front_page, :enterprise_ids attr_accessible :owner_id + attr_accessible :permalink attr_accessible :logo, :promo_image attr_accessible :address_attributes attr_accessible :email, :website, :facebook, :instagram, :linkedin, :twitter diff --git a/app/views/admin/enterprise_groups/_form_primary_details.html.haml b/app/views/admin/enterprise_groups/_form_primary_details.html.haml index 6d326e33fa..d57333626f 100644 --- a/app/views/admin/enterprise_groups/_form_primary_details.html.haml +++ b/app/views/admin/enterprise_groups/_form_primary_details.html.haml @@ -19,3 +19,8 @@ = f.label :enterprise_ids, 'Enterprises' %br/ = f.collection_select :enterprise_ids, @enterprises, :id, :name, {}, {class: "select2 fullwidth", multiple: true} + + = f.field_container :permalink do + = f.label :permalink, "Permalink (unique, no spaces)" + %br/ + = f.text_field :permalink From e4f93863fd5ce50cb089af4d43a8008a8bb24ca2 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 28 May 2015 10:17:09 +1000 Subject: [PATCH 134/151] Finding unique permalink before validation. --- app/models/enterprise.rb | 2 +- app/models/enterprise_group.rb | 25 +++++++++++++ spec/factories.rb | 1 - spec/models/enterprise_group_spec.rb | 55 ++++++++++++++++++++++++++++ spec/models/enterprise_spec.rb | 5 +++ 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index e921167cf0..067e800e3c 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -299,7 +299,7 @@ class Enterprise < ActiveRecord::Base test_permalink = test_permalink.parameterize test_permalink = "my-enterprise" if test_permalink.blank? existing = Enterprise.select(:permalink).order(:permalink).where("permalink LIKE ?", "#{test_permalink}%").map(&:permalink) - if existing.empty? + unless existing.include?(test_permalink) test_permalink else used_indices = existing.map do |p| diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 1524a80b53..a59b641d7c 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -15,6 +15,8 @@ class EnterpriseGroup < ActiveRecord::Base validates :name, presence: true validates :description, presence: true + + before_validation :sanitize_permalink validates :permalink, uniqueness: true, presence: true attr_accessible :name, :description, :long_description, :on_front_page, :enterprise_ids @@ -73,4 +75,27 @@ class EnterpriseGroup < ActiveRecord::Base address.zipcode.sub!(/^undefined$/, '') end + private + + def self.find_available_value(existing, requested) + return requested unless existing.include?(requested) + used_indices = existing.map do |p| + p.slice!(/^#{requested}/) + p.match(/^\d+$/).to_s.to_i + end + options = (1..used_indices.length + 1).to_a - used_indices + requested + options.first.to_s + end + + def find_available_permalink(requested) + existing = self.class.where(id: !id).where("permalink LIKE ?", "#{requested}%").pluck(:permalink) + self.class.find_available_value(existing, requested) + end + + def sanitize_permalink + if permalink.blank? || permalink_changed? + requested = permalink.presence || permalink_was.presence || name.presence || 'group' + self.permalink = find_available_permalink(requested.parameterize) + end + end end diff --git a/spec/factories.rb b/spec/factories.rb index f61172dc2a..abaecffa47 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -97,7 +97,6 @@ FactoryGirl.define do factory :enterprise, :class => Enterprise do owner { FactoryGirl.create :user } sequence(:name) { |n| "Enterprise #{n}" } - sequence(:permalink) { |n| "enterprise#{n}" } sells 'any' description 'enterprise' long_description '

Hello, world!

This is a paragraph.

' diff --git a/spec/models/enterprise_group_spec.rb b/spec/models/enterprise_group_spec.rb index ceca13b13b..45e7b0692c 100644 --- a/spec/models/enterprise_group_spec.rb +++ b/spec/models/enterprise_group_spec.rb @@ -2,11 +2,32 @@ require 'spec_helper' describe EnterpriseGroup do describe "validations" do + it "pass with name, description and address" do + e = EnterpriseGroup.new + e.name = 'Test Group' + e.description = 'A valid test group.' + e.address = build(:address) + e.should be_valid + end + it "is valid when built from factory" do e = build(:enterprise_group) e.should be_valid end + it "replace empty permalink and pass" do + e = build(:enterprise_group, permalink: '') + e.should be_valid + e.permalink.should == e.name.parameterize + end + + it "restores permalink and pass" do + e = create(:enterprise_group, permalink: 'p') + e.permalink = '' + e.should be_valid + e.permalink.should == 'p' + end + it "requires a name" do e = build(:enterprise_group, name: '') e.should_not be_valid @@ -60,5 +81,39 @@ describe EnterpriseGroup do EnterpriseGroup.managed_by(user).should == [eg1] end + + describe "finding a permalink" do + it "finds available permalink" do + existing = [] + expect(EnterpriseGroup.find_available_value(existing, "permalink")).to eq "permalink" + end + + it "finds available permalink similar to existing" do + existing = ["permalink1"] + expect(EnterpriseGroup.find_available_value(existing, "permalink")).to eq "permalink" + end + + it "adds unique number to existing permalinks" do + existing = ["permalink"] + expect(EnterpriseGroup.find_available_value(existing, "permalink")).to eq "permalink1" + existing = ["permalink", "permalink1"] + expect(EnterpriseGroup.find_available_value(existing, "permalink")).to eq "permalink2" + end + + it "ignores permalinks with characters after the index value" do + existing = ["permalink", "permalink1", "permalink2xxx"] + expect(EnterpriseGroup.find_available_value(existing, "permalink")).to eq "permalink2" + end + + it "finds gaps in the indices of existing permalinks" do + existing = ["permalink", "permalink1", "permalink3"] + expect(EnterpriseGroup.find_available_value(existing, "permalink")).to eq "permalink2" + end + + it "finds available indexed permalink" do + existing = ["permalink", "permalink1"] + expect(EnterpriseGroup.find_available_value(existing, "permalink1")).to eq "permalink11" + end + end end end diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index ed4669c0c2..c81c3818df 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -845,6 +845,11 @@ describe Enterprise do expect(Enterprise.find_available_permalink("permalink")).to eq "permalink2" end + it "finds available permalink similar to existing" do + create(:enterprise, permalink: "permalink2xxx") + expect(Enterprise.find_available_permalink("permalink2")).to eq "permalink2" + end + it "finds gaps in the indices of existing permalinks" do create(:enterprise, permalink: "permalink3") expect(Enterprise.find_available_permalink("permalink")).to eq "permalink2" From ff2eed77601d0813605e703ef77f3a224faabc6d Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 28 May 2015 11:27:06 +1000 Subject: [PATCH 135/151] Using permalink in URLs pointing to groups --- app/controllers/admin/enterprise_groups_controller.rb | 10 ++++++++-- app/controllers/groups_controller.rb | 2 +- app/models/enterprise_group.rb | 4 ++++ app/views/groups/index.html.haml | 2 +- app/views/json/_groups.rabl | 2 +- app/views/shopping_shared/_groups.html.haml | 2 +- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/controllers/admin/enterprise_groups_controller.rb b/app/controllers/admin/enterprise_groups_controller.rb index 3f8888edfb..cb3ac80935 100644 --- a/app/controllers/admin/enterprise_groups_controller.rb +++ b/app/controllers/admin/enterprise_groups_controller.rb @@ -9,7 +9,7 @@ module Admin def move_up EnterpriseGroup.with_isolation_level_serializable do - @enterprise_group = EnterpriseGroup.find params[:enterprise_group_id] + @enterprise_group = EnterpriseGroup.find_by_permalink params[:enterprise_group_id] @enterprise_group.move_higher end redirect_to main_app.admin_enterprise_groups_path @@ -17,7 +17,7 @@ module Admin def move_down EnterpriseGroup.with_isolation_level_serializable do - @enterprise_group = EnterpriseGroup.find params[:enterprise_group_id] + @enterprise_group = EnterpriseGroup.find_by_permalink params[:enterprise_group_id] @enterprise_group.move_lower end redirect_to main_app.admin_enterprise_groups_path @@ -33,6 +33,12 @@ module Admin end alias_method_chain :build_resource, :address + # Overriding method on Spree's resource controller, + # so that resources are found using permalink + def find_resource + EnterpriseGroup.find_by_permalink(params[:id]) + end + private def load_data diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 8653131b5a..fe43f0a0fa 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -7,6 +7,6 @@ class GroupsController < BaseController end def show - @group = EnterpriseGroup.find params[:id] + @group = EnterpriseGroup.find_by_permalink(params[:id]) || EnterpriseGroup.find(params[:id]) end end diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index a59b641d7c..986dc07d22 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -75,6 +75,10 @@ class EnterpriseGroup < ActiveRecord::Base address.zipcode.sub!(/^undefined$/, '') end + def to_param + permalink + end + private def self.find_available_value(existing, requested) diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 901cb17ad9..710039c7fb 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -20,7 +20,7 @@ .row.pad-top{bindonce: true} .small-12.medium-6.columns .groups-header - %a{"bo-href-i" => "/groups/{{group.id}}"} + %a{"bo-href-i" => "/groups/{{group.permalink}}"} %i.ofn-i_035-groups %span.group-name{"bo-text" => "group.name"} .small-3.medium-2.columns diff --git a/app/views/json/_groups.rabl b/app/views/json/_groups.rabl index a079290e23..bc50586a62 100644 --- a/app/views/json/_groups.rabl +++ b/app/views/json/_groups.rabl @@ -1,5 +1,5 @@ collection @groups -attributes :id, :name, :position, :description, :long_description, :email, :website, :facebook, :instagram, :linkedin, :twitter +attributes :id, :permalink, :name, :position, :description, :long_description, :email, :website, :facebook, :instagram, :linkedin, :twitter child enterprises: :enterprises do attributes :id diff --git a/app/views/shopping_shared/_groups.html.haml b/app/views/shopping_shared/_groups.html.haml index 68c68882fb..d94d61359c 100644 --- a/app/views/shopping_shared/_groups.html.haml +++ b/app/views/shopping_shared/_groups.html.haml @@ -9,5 +9,5 @@ %ul.bullet-list - for group in current_distributor.groups %li - %a{href: main_app.groups_path + "/#/#group#{group.id}"} + %a{href: main_app.groups_path + "/#{group.permalink}"} = group.name From 835b56b2220d25dc0f767d52ad7a7c9f8fca722d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 May 2015 09:44:57 +1000 Subject: [PATCH 136/151] Attempt to fix intermittent failures in spec/features/consumer/authentication_spec.rb --- spec/features/consumer/authentication_spec.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb index d0302bcd6f..bfa9f141fe 100644 --- a/spec/features/consumer/authentication_spec.rb +++ b/spec/features/consumer/authentication_spec.rb @@ -3,6 +3,11 @@ require 'spec_helper' feature "Authentication", js: true do include UIComponentHelper + # Attempt to address intermittent failures in these specs + around do |example| + Capybara.using_wait_time(120) { example.run } + end + describe "login" do let(:user) { create(:user, password: "password", password_confirmation: "password") } From cdbf02ca20f57b5d776bbe65422c620097e39476 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 May 2015 12:07:43 +1000 Subject: [PATCH 137/151] EnterpriseRelationship.relatives can find activated enterprises only --- app/models/enterprise_relationship.rb | 14 +++++++++----- spec/models/enterprise_relationship_spec.rb | 10 ++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index 22d04a6cd1..49c26cb0f8 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -28,7 +28,7 @@ class EnterpriseRelationship < ActiveRecord::Base # Load an array of the relatives of each enterprise (ie. any enterprise related to it in # either direction). This array is split into distributors and producers, and has the format: # {enterprise_id => {distributors: [id, ...], producers: [id, ...]} } - def self.relatives + def self.relatives(activated_only=false) relationships = EnterpriseRelationship.includes(:child, :parent) relatives = {} @@ -36,11 +36,15 @@ class EnterpriseRelationship < ActiveRecord::Base relatives[r.parent_id] ||= {distributors: [], producers: []} relatives[r.child_id] ||= {distributors: [], producers: []} - relatives[r.parent_id][:producers] << r.child_id if r.child.is_primary_producer - relatives[r.parent_id][:distributors] << r.child_id if r.child.is_distributor + if !activated_only || r.child.activated? + relatives[r.parent_id][:producers] << r.child_id if r.child.is_primary_producer + relatives[r.parent_id][:distributors] << r.child_id if r.child.is_distributor + end - relatives[r.child_id][:producers] << r.parent_id if r.parent.is_primary_producer - relatives[r.child_id][:distributors] << r.parent_id if r.parent.is_distributor + if !activated_only || r.parent.activated? + relatives[r.child_id][:producers] << r.parent_id if r.parent.is_primary_producer + relatives[r.child_id][:distributors] << r.parent_id if r.parent.is_distributor + end end relatives diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb index cb1743dec1..59e36c37e5 100644 --- a/spec/models/enterprise_relationship_spec.rb +++ b/spec/models/enterprise_relationship_spec.rb @@ -80,5 +80,15 @@ describe EnterpriseRelationship do {e1.id => {distributors: [e2.id], producers: [e2.id]}, e2.id => {distributors: [], producers: [e1.id]}} end + + it "finds inactive enterprises by default" do + e1.update_attribute :confirmed_at, nil + EnterpriseRelationship.relatives[e2.id][:producers].should == [e1.id] + end + + it "does not find inactive enterprises when requested" do + e1.update_attribute :confirmed_at, nil + EnterpriseRelationship.relatives(true)[e2.id][:producers].should be_empty + end end end From 69c54e1d704ebd15f5b935e0c93be619b24dc456 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 May 2015 12:08:21 +1000 Subject: [PATCH 138/151] Only load activated relatives for EnterpriseInjectionData --- app/models/enterprise.rb | 4 ++++ .../enterprise_injection_data.rb | 2 +- .../enterprise_injection_data_spec.rb | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 spec/lib/open_food_network/enterprise_injection_data_spec.rb diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index e921167cf0..61d441fb2e 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -178,6 +178,10 @@ class Enterprise < ActiveRecord::Base count(distinct: true) end + def activated? + confirmed_at.present? && sells != 'unspecified' + end + def set_producer_property(property_name, property_value) transaction do property = Spree::Property.where(name: property_name).first_or_create!(presentation: property_name) diff --git a/lib/open_food_network/enterprise_injection_data.rb b/lib/open_food_network/enterprise_injection_data.rb index 9862418b98..87516007c6 100644 --- a/lib/open_food_network/enterprise_injection_data.rb +++ b/lib/open_food_network/enterprise_injection_data.rb @@ -13,7 +13,7 @@ module OpenFoodNetwork end def relatives - @relatives ||= EnterpriseRelationship.relatives + @relatives ||= EnterpriseRelationship.relatives(true) end def supplied_taxons diff --git a/spec/lib/open_food_network/enterprise_injection_data_spec.rb b/spec/lib/open_food_network/enterprise_injection_data_spec.rb new file mode 100644 index 0000000000..cb94f2374a --- /dev/null +++ b/spec/lib/open_food_network/enterprise_injection_data_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +module OpenFoodNetwork + describe EnterpriseInjectionData do + describe "relatives" do + let!(:enterprise) { create(:distributor_enterprise) } + let!(:producer) { create(:supplier_enterprise) } + let!(:producer_inactive) { create(:supplier_enterprise, confirmed_at: nil) } + let!(:er_p) { create(:enterprise_relationship, parent: producer, child: enterprise) } + let!(:er_pi) { create(:enterprise_relationship, parent: producer_inactive, child: enterprise) } + + it "only loads activated relatives" do + subject.relatives[enterprise.id][:producers].should_not include producer_inactive.id + end + end + end +end From 3f4f8afacd8b8ca432861da0fa8f6b92e68953df Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 May 2015 12:19:38 +1000 Subject: [PATCH 139/151] EnterpriseRelationship.relatives does not show duplicates --- app/models/enterprise_relationship.rb | 4 ++-- spec/models/enterprise_relationship_spec.rb | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index 49c26cb0f8..de06a0578e 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -33,8 +33,8 @@ class EnterpriseRelationship < ActiveRecord::Base relatives = {} relationships.each do |r| - relatives[r.parent_id] ||= {distributors: [], producers: []} - relatives[r.child_id] ||= {distributors: [], producers: []} + relatives[r.parent_id] ||= {distributors: Set.new, producers: Set.new} + relatives[r.child_id] ||= {distributors: Set.new, producers: Set.new} if !activated_only || r.child.activated? relatives[r.parent_id][:producers] << r.child_id if r.child.is_primary_producer diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb index 59e36c37e5..96f80a65a4 100644 --- a/spec/models/enterprise_relationship_spec.rb +++ b/spec/models/enterprise_relationship_spec.rb @@ -74,21 +74,27 @@ describe EnterpriseRelationship do let(:e1) { create(:supplier_enterprise) } let(:e2) { create(:supplier_enterprise, sells: 'any') } let!(:er) { create(:enterprise_relationship, parent: e1, child: e2) } + let(:er_reverse) { create(:enterprise_relationship, parent: e2, child: e1) } it "categorises enterprises into distributors and producers" do EnterpriseRelationship.relatives.should == - {e1.id => {distributors: [e2.id], producers: [e2.id]}, - e2.id => {distributors: [], producers: [e1.id]}} + {e1.id => {distributors: Set.new([e2.id]), producers: Set.new([e2.id])}, + e2.id => {distributors: Set.new([]), producers: Set.new([e1.id])}} end it "finds inactive enterprises by default" do e1.update_attribute :confirmed_at, nil - EnterpriseRelationship.relatives[e2.id][:producers].should == [e1.id] + EnterpriseRelationship.relatives[e2.id][:producers].should == Set.new([e1.id]) end it "does not find inactive enterprises when requested" do e1.update_attribute :confirmed_at, nil EnterpriseRelationship.relatives(true)[e2.id][:producers].should be_empty end + + it "does not show duplicates" do + er_reverse + EnterpriseRelationship.relatives[e2.id][:producers].should == Set.new([e1.id]) + end end end From d478cc1f69a1ed7d4436ef5e323acaec5de5d39c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 May 2015 14:03:44 +1000 Subject: [PATCH 140/151] Serialize taxons and relatives in expected format --- app/serializers/api/enterprise_serializer.rb | 16 ++++++++---- .../serializers/enterprise_serializer_spec.rb | 25 +++++++++++++------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index fc843a5138..44364c4dbc 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -3,7 +3,6 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer Api::IdSerializer def serializable_hash - cached_serializer_hash.merge uncached_serializer_hash end @@ -51,11 +50,11 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer def taxons - options[:data].distributed_taxons[object.id] + ids_to_objs options[:data].distributed_taxons[object.id] end def supplied_taxons - options[:data].supplied_taxons[object.id] + ids_to_objs options[:data].supplied_taxons[object.id] end def pickup @@ -90,12 +89,12 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer def producers relatives = options[:data].relatives[object.id] - relatives ? relatives[:producers] : [] + relatives ? ids_to_objs(relatives[:producers]) : [] end def hubs relatives = options[:data].relatives[object.id] - relatives ? relatives[:distributors] : [] + relatives ? ids_to_objs(relatives[:distributors]) : [] end # Map svg icons. @@ -135,4 +134,11 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer } icon_fonts[object.category] end + + + private + + def ids_to_objs(ids) + ids.andand.map { |id| {id: id} } + end end diff --git a/spec/serializers/enterprise_serializer_spec.rb b/spec/serializers/enterprise_serializer_spec.rb index 7c467d9c3d..f70852d973 100644 --- a/spec/serializers/enterprise_serializer_spec.rb +++ b/spec/serializers/enterprise_serializer_spec.rb @@ -1,20 +1,31 @@ #require 'spec_helper' describe Api::EnterpriseSerializer do + let(:serializer) { Api::EnterpriseSerializer.new enterprise, data: data } let(:enterprise) { create(:distributor_enterprise) } let(:taxon) { create(:taxon) } - let(:data_class) { Struct.new(:earliest_closing_times, :active_distributors, - :distributed_taxons, :supplied_taxons, - :shipping_method_services, :relatives) } - let(:data) { data_class.new({}, [], {}, {}, {}, {producers: [], distributors: []}) } + let(:data) { OpenStruct.new(earliest_closing_times: {}, + active_distributors: [], + distributed_taxons: {enterprise.id => [123]}, + supplied_taxons: {enterprise.id => [456]}, + shipping_method_services: {}, + relatives: {enterprise.id => {producers: [123], distributors: [456]}}) } it "serializes an enterprise" do - serializer = Api::EnterpriseSerializer.new enterprise, data: data serializer.to_json.should match enterprise.name end - it "will render urls" do - serializer = Api::EnterpriseSerializer.new enterprise, data: data + it "serializes taxons as ids only" do + serializer.serializable_hash[:taxons].should == [{id: 123}] + serializer.serializable_hash[:supplied_taxons].should == [{id: 456}] + end + + it "serializes producers and hubs as ids only" do + serializer.serializable_hash[:producers].should == [{id: 123}] + serializer.serializable_hash[:hubs].should == [{id: 456}] + end + + it "serializes icons" do serializer.to_json.should match "map_005-hub.svg" end end From 50ae331d9459af558319558ea913b5a5562aaa5c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 May 2015 16:03:16 +1000 Subject: [PATCH 141/151] ng-cloak mobile menu --- app/views/shared/menu/_mobile_menu.html.haml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/shared/menu/_mobile_menu.html.haml b/app/views/shared/menu/_mobile_menu.html.haml index 5ed57917fd..e5ba9af83b 100644 --- a/app/views/shared/menu/_mobile_menu.html.haml +++ b/app/views/shared/menu/_mobile_menu.html.haml @@ -2,7 +2,7 @@ %section.left %a.left-off-canvas-toggle.menu-icon %span - %section.right + %section.right{"ng-cloak" => true} .cart = render partial: "shared/menu/cart" %a{href: main_app.shop_path} @@ -11,34 +11,34 @@ %aside.left-off-canvas-menu.show-for-medium-down %ul.off-canvas-list %li.ofn-logo - %a{href: root_path} + %a{href: root_path} %img{src: "/assets/open-food-network-beta.png", srcset: "/assets/open-food-network-beta.svg", width: "110", height: "26"} - + - if current_page? root_path %li.li-menu %a{"ofn-scroll-to" => "hubs"} - %span.nav-primary + %span.nav-primary %i.ofn-i_040-hub Hubs - else %li.li-menu %a{href: root_path + "#/#hubs"} - %span.nav-primary + %span.nav-primary %i.ofn-i_040-hub Hubs %li.li-menu %a{href: main_app.map_path} - %span.nav-primary + %span.nav-primary %i.ofn-i_037-map Map %li.li-menu %a{href: main_app.producers_path} - %span.nav-primary + %span.nav-primary %i.ofn-i_036-producers Producers %li.li-menu %a{href: main_app.groups_path} - %span.nav-primary + %span.nav-primary %i.ofn-i_035-groups Groups From 5c3a59acabaf2a340e1fc084e221eeac5cba84ad Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 May 2015 16:39:41 +1000 Subject: [PATCH 142/151] ng-cloak order cycles selector, tabs and shopfront --- app/views/enterprises/shop.html.haml | 2 +- app/views/shop/products/_form.html.haml | 2 +- app/views/shopping_shared/_tabs.html.haml | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/enterprises/shop.html.haml b/app/views/enterprises/shop.html.haml index 89ce1a30ac..08e25417cb 100644 --- a/app/views/enterprises/shop.html.haml +++ b/app/views/enterprises/shop.html.haml @@ -3,7 +3,7 @@ %shop.darkswarm - content_for :order_cycle_form do - %div{"ng-controller" => "OrderCycleChangeCtrl"} + %div{"ng-controller" => "OrderCycleChangeCtrl", "ng-cloak" => true} %closing{"ng-if" => "OrderCycle.selected()"} Next order closing %strong {{ OrderCycle.orders_close_at() | date_in_words }} diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index 0df4f022aa..bdaded0f0d 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -1,4 +1,4 @@ -%products.small-12.columns{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null", +%products.small-12.columns{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null", "ng-cloak" => true, "infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1"} // TODO: Needs an ng-show to slide content down diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 3641e0cbd5..48c0a1d7b5 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -1,11 +1,11 @@ -#tabs{"ng-controller" => "TabsCtrl"} +#tabs{"ng-controller" => "TabsCtrl", "ng-cloak" => true} .row %tabset -# Build all tabs. - - for name, heading_cols in { about: ["About #{current_distributor.name}", 6], - producers: ["Producers",2], + - for name, heading_cols in { about: ["About #{current_distributor.name}", 6], + producers: ["Producers",2], contact: ["Contact",2], - groups: ["Groups",2]} + groups: ["Groups",2]} -# tabs take tab path in 'active' and 'select' functions defined in TabsCtrl. - heading, cols = heading_cols %tab.columns{heading: heading, From 0d3cdb9c694b0346912acf0064b008b5b2cfa17a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 29 May 2015 16:51:55 +1000 Subject: [PATCH 143/151] Expand All button to show all variants in BPE --- .../javascripts/admin/bulk_product_update.js.coffee | 6 ++++++ .../admin/directives/toggle_variants.js.coffee | 4 +--- .../admin/services/display_properties.js.coffee | 8 +++----- .../admin/products/bulk_edit/_products_head.html.haml | 2 ++ .../admin/products/bulk_edit/_products_product.html.haml | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index c902328af4..c557c0c791 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -109,6 +109,12 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout window.location = "/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit" + $scope.toggleShowAllVariants = -> + showVariants = !DisplayProperties.showVariants 0 + $scope.filteredProducts.forEach (product) -> + DisplayProperties.setShowVariants product.id, showVariants + DisplayProperties.setShowVariants 0, showVariants + $scope.addVariant = (product) -> product.variants.push id: $scope.nextVariantId() diff --git a/app/assets/javascripts/admin/directives/toggle_variants.js.coffee b/app/assets/javascripts/admin/directives/toggle_variants.js.coffee index 410df8d7e9..f5b18ae5cb 100644 --- a/app/assets/javascripts/admin/directives/toggle_variants.js.coffee +++ b/app/assets/javascripts/admin/directives/toggle_variants.js.coffee @@ -1,10 +1,8 @@ angular.module("ofn.admin").directive "ofnToggleVariants", (DisplayProperties) -> link: (scope, element, attrs) -> if DisplayProperties.showVariants scope.product.id - element.removeClass "icon-chevron-right" element.addClass "icon-chevron-down" else - element.removeClass "icon-chevron-down" element.addClass "icon-chevron-right" element.on "click", -> @@ -16,4 +14,4 @@ angular.module("ofn.admin").directive "ofnToggleVariants", (DisplayProperties) - else DisplayProperties.setShowVariants scope.product.id, true element.removeClass "icon-chevron-right" - element.addClass "icon-chevron-down" \ No newline at end of file + element.addClass "icon-chevron-down" diff --git a/app/assets/javascripts/admin/services/display_properties.js.coffee b/app/assets/javascripts/admin/services/display_properties.js.coffee index 7288706032..3037c9f068 100644 --- a/app/assets/javascripts/admin/services/display_properties.js.coffee +++ b/app/assets/javascripts/admin/services/display_properties.js.coffee @@ -3,12 +3,10 @@ angular.module("ofn.admin").factory "DisplayProperties", -> displayProperties: {} showVariants: (product_id) -> - @initProduct product_id - @displayProperties[product_id].showVariants + @productProperties(product_id).showVariants setShowVariants: (product_id, showVariants) -> - @initProduct product_id - @displayProperties[product_id].showVariants = showVariants + @productProperties(product_id).showVariants = showVariants - initProduct: (product_id) -> + productProperties: (product_id) -> @displayProperties[product_id] ||= {showVariants: false} diff --git a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml index 6e22aef8ff..97b4f47c83 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml @@ -19,6 +19,8 @@ %thead %tr %th.left-actions + %a{ 'ng-click' => 'toggleShowAllVariants()', :style => 'color: red' } + Expand All %th.producer{ 'ng-show' => 'columns.producer.visible' } Producer %th.sku{ 'ng-show' => 'columns.sku.visible' } SKU %th.name{ 'ng-show' => 'columns.name.visible' } Name diff --git a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml index 376e88071a..6ac25ae286 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml @@ -1,6 +1,6 @@ %tr.product{ :id => "p_{{product.id}}" } %td.left-actions - %a{ 'ofn-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' } + %a{ 'ofn-toggle-variants' => 'true', :class => "view-variants", 'ng-show' => 'hasVariants(product)' } %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "!hasVariants(product) && hasUnit(product)" } %td.producer{ 'ng-show' => 'columns.producer.visible' } %select.select2.fullwidth{ 'ng-model' => 'product.producer_id', :name => 'producer_id', 'ofn-track-product' => 'producer_id', 'ng-options' => 'producer.id as producer.name for producer in producers' } From 8afffdae9ab7d205475ede289213524c5ed14ac1 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 12:13:42 +1000 Subject: [PATCH 144/151] Fix error when product does not have a master variant --- app/assets/javascripts/darkswarm/services/products.js.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/products.js.coffee b/app/assets/javascripts/darkswarm/services/products.js.coffee index a07ae1f466..4785adae85 100644 --- a/app/assets/javascripts/darkswarm/services/products.js.coffee +++ b/app/assets/javascripts/darkswarm/services/products.js.coffee @@ -32,8 +32,9 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro if product.variants product.variants = (Variants.register variant for variant in product.variants) variant.product = product for variant in product.variants - product.master.product = product - product.master = Variants.register product.master if product.master + if product.master + product.master.product = product + product.master = Variants.register product.master registerVariantsWithCart: -> for product in @products From 473322c7e6b14a4c24927bff36440364d9138206 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 12:25:28 +1000 Subject: [PATCH 145/151] CI: Add more robust merge-to-master script --- script/ci/merge_branch_to_master.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/script/ci/merge_branch_to_master.sh b/script/ci/merge_branch_to_master.sh index 3eaae17d56..fd9c602802 100755 --- a/script/ci/merge_branch_to_master.sh +++ b/script/ci/merge_branch_to_master.sh @@ -6,5 +6,9 @@ source ./script/ci/includes.sh echo "--- Verifying branch is based on current master" exit_unless_master_merged -echo "--- Pushing branch" -git push origin $BUILDKITE_COMMIT:master +echo "--- Merging and pushing branch" +git checkout master +git merge origin/master +git merge origin/$BUILDKITE_BRANCH +git push origin master +git checkout origin/$BUILDKITE_BRANCH From c6f6c11a43143d3c820be511b8c940937696c49a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 12:51:15 +1000 Subject: [PATCH 146/151] Add wait between clicks to fix race condition --- spec/features/admin/bulk_product_update_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index 4c52ec2256..f5134f4e81 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -205,8 +205,9 @@ feature %q{ expect(page).to have_selector "a.edit-variant", count: 1 # When I remove two, they should be removed - page.all('a.delete-variant').first.click - page.all('a.delete-variant').first.click + page.all('a.delete-variant', visible: true).first.click + expect(page).to have_selector "tr.variant", count: 2 + page.all('a.delete-variant', visible: true).first.click expect(page).to have_selector "tr.variant", count: 1 # When I fill out variant details and hit update From b3878b126b3c73b6baa1cefd35feb06e6813ce7a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 12:53:46 +1000 Subject: [PATCH 147/151] Decouple generic injection spec from EnterpriseSerializer --- spec/helpers/injection_helper_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helpers/injection_helper_spec.rb b/spec/helpers/injection_helper_spec.rb index 96d9279ef5..362ca6479b 100644 --- a/spec/helpers/injection_helper_spec.rb +++ b/spec/helpers/injection_helper_spec.rb @@ -4,7 +4,7 @@ describe InjectionHelper do let!(:enterprise) { create(:distributor_enterprise, facebook: "roger") } it "will inject via AMS" do - helper.inject_json_ams("test", [enterprise], Api::EnterpriseSerializer).should match enterprise.name + helper.inject_json_ams("test", [enterprise], Api::IdSerializer).should match /#{enterprise.id}/ end it "injects enterprises" do From 36dc0d5ccd767446a63c9b4dd07bfb4f41a41c5b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 13:00:07 +1000 Subject: [PATCH 148/151] Do not run performance specs in CI --- script/ci/run_tests.sh | 2 +- spec/performance/injection_helper_spec.rb | 2 +- spec/performance/shop_controller_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/script/ci/run_tests.sh b/script/ci/run_tests.sh index 5539935a8b..f973c139ad 100755 --- a/script/ci/run_tests.sh +++ b/script/ci/run_tests.sh @@ -16,4 +16,4 @@ echo "--- Loading test database" bundle exec rake db:test:load echo "--- Running tests" -bundle exec rspec spec +bundle exec rspec --tag ~performance spec diff --git a/spec/performance/injection_helper_spec.rb b/spec/performance/injection_helper_spec.rb index 4ba22d738f..ef8d937ce1 100644 --- a/spec/performance/injection_helper_spec.rb +++ b/spec/performance/injection_helper_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe InjectionHelper, type: :helper do +describe InjectionHelper, type: :helper, performance: true do let(:oc) { create(:simple_order_cycle) } let(:relative_supplier) { create(:supplier_enterprise) } let(:relative_distributor) { create(:distributor_enterprise) } diff --git a/spec/performance/shop_controller_spec.rb b/spec/performance/shop_controller_spec.rb index 14e258d18d..984581a2ab 100644 --- a/spec/performance/shop_controller_spec.rb +++ b/spec/performance/shop_controller_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ShopController, type: :controller do +describe ShopController, type: :controller, performance: true do let(:d) { create(:distributor_enterprise) } let(:enterprise_fee) { create(:enterprise_fee) } let(:order_cycle) { create(:simple_order_cycle, distributors: [d], coordinator_fees: [enterprise_fee]) } From db47c01784df955cff00560f3c4b5454467025dd Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 1 May 2015 09:50:12 +1000 Subject: [PATCH 149/151] Initial config for parallel spec running --- .rspec_parallel | 2 ++ Gemfile | 1 + Gemfile.lock | 4 ++++ config/database.yml | 2 +- 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .rspec_parallel diff --git a/.rspec_parallel b/.rspec_parallel new file mode 100644 index 0000000000..54b33804ae --- /dev/null +++ b/.rspec_parallel @@ -0,0 +1,2 @@ +--format progress +--format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log diff --git a/Gemfile b/Gemfile index b63961a3ef..3a698cf366 100644 --- a/Gemfile +++ b/Gemfile @@ -112,4 +112,5 @@ group :development do gem 'guard-rails' gem 'guard-zeus' gem 'guard-rspec' + gem 'parallel_tests' end diff --git a/Gemfile.lock b/Gemfile.lock index 1b62201120..09c5f866f3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -362,6 +362,9 @@ GEM activesupport (>= 3.0.0) cocaine (~> 0.5.3) mime-types + parallel (1.4.1) + parallel_tests (1.3.7) + parallel paypal-sdk-core (0.2.10) multi_json (~> 1.0) xml-simple @@ -575,6 +578,7 @@ DEPENDENCIES newrelic_rpm oj paperclip + parallel_tests pg poltergeist pry-debugger diff --git a/config/database.yml b/config/database.yml index d74ed6256a..7eef2396f9 100644 --- a/config/database.yml +++ b/config/database.yml @@ -10,7 +10,7 @@ development: test: adapter: postgresql encoding: unicode - database: open_food_network_test + database: open_food_network_test<%= ENV['TEST_ENV_NUMBER'] %> pool: 5 host: localhost username: ofn From 2b3689fd933f99349f5284c61fc51ff1e875fb34 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 14:29:24 +1000 Subject: [PATCH 150/151] Run CI specs in parallel --- script/ci/run_tests.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script/ci/run_tests.sh b/script/ci/run_tests.sh index 5539935a8b..9f9cdfa30d 100755 --- a/script/ci/run_tests.sh +++ b/script/ci/run_tests.sh @@ -13,7 +13,8 @@ echo "--- Bundling" bundle install echo "--- Loading test database" -bundle exec rake db:test:load +bundle exec rake db:drop db:create db:schema:load +bundle exec rake parallel:drop parallel:create parallel:load_schema echo "--- Running tests" -bundle exec rspec spec +bundle exec rake parallel:spec From 73029636055c609959feeca03d29a621eb1a1124 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 3 Jun 2015 15:11:32 +1000 Subject: [PATCH 151/151] inventory report: filter was broken because filter_to_order_cycle returned nil [skip ci] --- .../products_and_inventory_report.rb | 14 ++------------ .../products_and_inventory_report_spec.rb | 12 +----------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/lib/open_food_network/products_and_inventory_report.rb b/lib/open_food_network/products_and_inventory_report.rb index 163f9a4f50..2b797bd944 100644 --- a/lib/open_food_network/products_and_inventory_report.rb +++ b/lib/open_food_network/products_and_inventory_report.rb @@ -47,7 +47,7 @@ module OpenFoodNetwork end def variants - filter(child_variants) + filter(master_variants) + filter(child_variants) end def child_variants @@ -57,16 +57,6 @@ module OpenFoodNetwork .order("spree_products.name") end - def master_variants - Spree::Variant.where(:is_master => true) - .joins(:product) - .where("(select spree_variants.id from spree_variants as other_spree_variants - WHERE other_spree_variants.product_id = spree_variants.product_id - AND other_spree_variants.is_master = 'f' LIMIT 1) IS NULL") - .merge(visible_products) - .order("spree_products.name") - end - def filter(variants) # NOTE: Ordering matters. # filter_to_order_cycle and filter_to_distributor return Arrays not Arel @@ -107,7 +97,7 @@ module OpenFoodNetwork def filter_to_order_cycle(variants) if params[:order_cycle_id].to_i > 0 order_cycle = OrderCycle.find params[:order_cycle_id] - variants.select! { |v| order_cycle.variants.include? v } + variants.select { |v| order_cycle.variants.include? v } else variants end diff --git a/spec/lib/open_food_network/products_and_inventory_report_spec.rb b/spec/lib/open_food_network/products_and_inventory_report_spec.rb index 368eb4d4d7..13796c10f6 100644 --- a/spec/lib/open_food_network/products_and_inventory_report_spec.rb +++ b/spec/lib/open_food_network/products_and_inventory_report_spec.rb @@ -54,10 +54,8 @@ module OpenFoodNetwork it "fetches variants for some params" do subject.should_receive(:child_variants).and_return ["children"] - subject.should_receive(:master_variants).and_return ["masters"] subject.should_receive(:filter).with(['children']).and_return ["filter_children"] - subject.should_receive(:filter).with(['masters']).and_return ["filter_masters"] - subject.variants.should == ["filter_children", "filter_masters"] + subject.variants.should == ["filter_children"] end end @@ -92,14 +90,6 @@ module OpenFoodNetwork end end - describe "fetching master variants" do - it "doesn't return master variants with siblings" do - product = create(:simple_product, supplier: supplier) - - subject.master_variants.should be_empty - end - end - describe "Filtering variants" do let(:variants) { Spree::Variant.scoped.joins(:product).where(is_master: false) } it "should return unfiltered variants sans-params" do