From 16ab8f04c304556c07c8847d3ef52824368942be Mon Sep 17 00:00:00 2001 From: stveep Date: Fri, 18 Dec 2015 17:06:04 +0000 Subject: [PATCH 001/215] Angularise /account page: Order service/controller, rough layout --- .../distributor_node_controller.js.coffee | 8 +++ .../controllers/orders_controller.js.coffee | 8 +++ .../darkswarm/services/orders.js.coffee | 14 +++++ app/helpers/injection_helper.rb | 4 ++ app/serializers/api/order_serializer.rb | 10 ++++ app/views/spree/users/_fat.html.haml | 3 + app/views/spree/users/_skinny.html.haml | 14 +++++ app/views/spree/users/show.html.haml | 56 ++++++++----------- 8 files changed, 83 insertions(+), 34 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee create mode 100644 app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee create mode 100644 app/assets/javascripts/darkswarm/services/orders.js.coffee create mode 100644 app/serializers/api/order_serializer.rb create mode 100644 app/views/spree/users/_fat.html.haml create mode 100644 app/views/spree/users/_skinny.html.haml diff --git a/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee new file mode 100644 index 0000000000..a260f81951 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee @@ -0,0 +1,8 @@ +Darkswarm.controller "DistributorNodeCtrl", ($scope, HashNavigation, $anchorScroll) -> + $scope.toggle = -> + HashNavigation.toggle $scope.distributor + $scope.open = -> + HashNavigation.active($scope.distributor) + + if $scope.open() + $anchorScroll() diff --git a/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee new file mode 100644 index 0000000000..f30cfa8436 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee @@ -0,0 +1,8 @@ +Darkswarm.controller "OrdersCtrl", ($scope, $rootScope, $timeout, Orders, Search, $document, HashNavigation, FilterSelectorsService, EnterpriseModal, enterpriseMatchesNameQueryFilter, distanceWithinKmFilter) -> + $scope.Orders = Orders + + $scope.filterEnterprises = -> + es = Enterprises.hubs + $scope.nameMatches = enterpriseMatchesNameQueryFilter(es, true) + $scope.distanceMatches = enterpriseMatchesNameQueryFilter(es, false) + $scope.distanceMatches = distanceWithinKmFilter($scope.distanceMatches, 50) diff --git a/app/assets/javascripts/darkswarm/services/orders.js.coffee b/app/assets/javascripts/darkswarm/services/orders.js.coffee new file mode 100644 index 0000000000..dbfcf207e3 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/orders.js.coffee @@ -0,0 +1,14 @@ +Darkswarm.factory 'Orders', (orders, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope)-> + new class Orders + orders_by_distributor: {} + distributors = [] + constructor: -> + # Populate Orders.orders from json in page. + @orders = orders + # Organise orders by distributor. + for order in orders + if order.distributor?.id + @orders_by_distributor[order.distributor.name] = order + # Can we guarantee order of keys in js? + @distributors = Object.keys(@orders_by_distributor) + # Sorting by most orders (most recent/frequent?) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 05057c136b..129c9aeed2 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -51,6 +51,10 @@ module InjectionHelper render partial: "json/injection_ams", locals: {name: 'enterpriseAttributes', json: "#{@enterprise_attributes.to_json}"} end + def inject_orders_for_user + inject_json_ams "orders", spree_current_user.orders, Api::OrderSerializer + end + def inject_json(name, partial, opts = {}) render partial: "json/injection", locals: {name: name, partial: partial}.merge(opts) end diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb new file mode 100644 index 0000000000..6648cb7cea --- /dev/null +++ b/app/serializers/api/order_serializer.rb @@ -0,0 +1,10 @@ +class Api::OrderSerializer < ActiveModel::Serializer + attributes :id, :completed_at, :total, :state, :shipment_state, :outstanding_balance + + has_one :distributor, serializer: Api::IdNameSerializer + + def completed_at + object.completed_at.blank? ? "" : object.completed_at.strftime("%F %T") + end + +end diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml new file mode 100644 index 0000000000..ce16555492 --- /dev/null +++ b/app/views/spree/users/_fat.html.haml @@ -0,0 +1,3 @@ +.row.active_table_row + .columns.small-12.medium-7.large-7.fat + %span{"bo-text" => "distributor"} diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml new file mode 100644 index 0000000000..b608bfcf98 --- /dev/null +++ b/app/views/spree/users/_skinny.html.haml @@ -0,0 +1,14 @@ +-# Add extra highlighting etc (credit/debit?) here +.row.active_table_row{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} + .columns.small-12.medium-4.large-4.skinny-head + %span.margin-top + %strong{"bo-text" => "distributor"} + + -# + .columns.small-6.medium-3.large-3 + %span.margin-top{"bo-text" => "producer.address.city"} + .columns.small-4.medium-3.large-4 + %span.margin-top{"bo-bind" => "producer.address.state_name | uppercase"} + .columns.small-2.medium-2.large-1.text-right + %span.margin-top + %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index 878d271e42..733c7aa6aa 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -1,34 +1,22 @@ -.darkswarm - .row.pad-top - .small-12.columns.pad-top - %h1= accurate_title - .account-summary{"data-hook" => "account_summary"} - %dl#user-info - %dt= t(:email) - %dd - = @user.email - (#{link_to t(:edit), spree.edit_account_path}) - .account-my-orders{"data-hook" => "account_my_orders"} - %h3= t(:my_orders) - - if @orders.present? - %table.order-summary - %thead - %tr - %th.order-number= t(:order_number) - %th.order-date= t(:order_date) - %th.order-status= t(:status) - %th.order-payment-state= t(:payment_state) - %th.order-shipment-state= t(:shipment_state) - %th.order-total= t(:total) - %tbody - - @orders.each do |order| - %tr{class: cycle('even', 'odd')} - %td.order-number= link_to order.number, order_url(order) - %td.order-date= l order.completed_at.to_date - %td.order-status= t(order.state).titleize - %td.order-payment-state= t("payment_states.#{order.payment_state}") if order.payment_state - %td.order-shipment-state= t("shipment_states.#{order.shipment_state}") if order.shipment_state - %td.order-total= money order.total - - else - %p= t(:you_have_no_orders_yet) - %br/ += inject_orders_for_user + +.row.pad-top + .small-12.columns.pad-top + %h1= accurate_title + .account-summary{"data-hook" => "account_summary"} + %dl#user-info + %dt= t(:email) + %dd + = @user.email + (#{link_to t(:edit), spree.edit_account_path}) + -# Add back ng-cloak below + .orders{"ng-controller" => "OrdersCtrl"} + .row{bindonce: true} + .small-12.columns + .active_table + %distributor.active_table_node.row.animate-repeat{id: "{{distributor}}", + "ng-repeat" => "distributor in Orders.distributors", + "ng-controller" => "DistributorNodeCtrl", + "ng-class" => "{'closed' : !open(), 'open' : open()}"} + .small-12.columns + = render partial: "spree/users/fat" From 2a2f8f3531ae797515bf664623c76072d2ff20a3 Mon Sep 17 00:00:00 2001 From: Edem Date: Sat, 12 Dec 2015 16:58:42 +0100 Subject: [PATCH 002/215] admin entreprise_group are translated! --- .../enterprise_groups/_form_about.html.haml | 3 ++- .../enterprise_groups/_form_address.html.haml | 20 +++++++------- .../enterprise_groups/_form_images.html.haml | 12 +++++---- .../_form_primary_details.html.haml | 7 ++--- .../enterprise_groups/_form_users.html.haml | 7 ++--- .../enterprise_groups/_form_web.html.haml | 7 ++--- .../admin/enterprise_groups/index.html.haml | 14 ++++++---- .../enterprise_relationships/index.html.haml | 2 +- config/locales/en.yml | 26 +++++++++++++++++++ 9 files changed, 67 insertions(+), 31 deletions(-) diff --git a/app/views/admin/enterprise_groups/_form_about.html.haml b/app/views/admin/enterprise_groups/_form_about.html.haml index a29fde22cd..2c2ba59464 100644 --- a/app/views/admin/enterprise_groups/_form_about.html.haml +++ b/app/views/admin/enterprise_groups/_form_about.html.haml @@ -1,5 +1,6 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='About'" } } - %legend About + %legend + = t 'admin_entreprise_groups_about' = f.field_container :long_description do %text-angular{'id' => 'enterprise_group_long_description', 'name' => 'enterprise_group[long_description]', 'class' => 'text-angular', 'ta-toolbar' => "[['h1','h2','h3','h4','p'],['bold','italics','underline','clear'],['insertLink']]"} diff --git a/app/views/admin/enterprise_groups/_form_address.html.haml b/app/views/admin/enterprise_groups/_form_address.html.haml index 12bb7ef315..a161c1d619 100644 --- a/app/views/admin/enterprise_groups/_form_address.html.haml +++ b/app/views/admin/enterprise_groups/_form_address.html.haml @@ -1,11 +1,12 @@ = f.fields_for :address do |af| %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Contact'" } } - %legend Contact + %legend + = t 'admin_entreprise_groups_contact' .row .alpha.three.columns = af.label :phone .omega.eight.columns - = af.text_field :phone, { placeholder: "eg. 98 7654 3210"} + = af.text_field :phone, { placeholder: t(:admin_entreprise_groups_contact_phone_placeholder)} .row .alpha.three.columns = f.label :email @@ -15,7 +16,7 @@ .three.columns.alpha = af.label :address1 .eight.columns.omega - = af.text_field :address1, { placeholder: "eg. 123 High Street"} + = af.text_field :address1, { placeholder: t(:admin_entreprise_groups_contact_address1_placeholder)} .row .alpha.three.columns = af.label :address2 @@ -23,18 +24,17 @@ = af.text_field :address2 .row .three.columns.alpha - = af.label :city, 'Suburb' + = af.label :city, t(:admin_entreprise_groups_contact_city) \/ - = af.label :zipcode, 'Postcode' + = af.label :zipcode, t(:admin_entreprise_groups_contact_zipcode) .four.columns - = af.text_field :city, { placeholder: "eg. Northcote"} + = af.text_field :city, { placeholder: t(:admin_entreprise_groups_contact_city_placeholder)} .four.columns.omega - = af.text_field :zipcode, { placeholder: "eg. 3070"} + = af.text_field :zipcode, { placeholder: t(:admin_entreprise_groups_contact_zipcode_placeholder)} .row .three.columns.alpha - = af.label :state_id, 'State' - \/ - = af.label :country_id, 'Country' + = af.label :state_id, t(:admin_entreprise_groups_contact_state_id) + = af.label :country_id, t(:admin_entreprise_groups_contact_country_id) .four.columns = af.collection_select :state_id, af.object.country.states, :id, :name, {}, :class => "select2 fullwidth" .four.columns.omega diff --git a/app/views/admin/enterprise_groups/_form_images.html.haml b/app/views/admin/enterprise_groups/_form_images.html.haml index 49169851c3..681c6c42b9 100644 --- a/app/views/admin/enterprise_groups/_form_images.html.haml +++ b/app/views/admin/enterprise_groups/_form_images.html.haml @@ -1,18 +1,20 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Images'" } } - %legend Images + %legend + = t 'admin_entreprise_groups_images' .row .alpha.three.columns = f.label :logo, class: 'with-tip', 'data-powertip' => 'This is the logo' - .with-tip{'data-powertip' => 'This is the logo'} + .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip_logo)} %a What's this? .omega.eight.columns = image_tag @object.logo.url if @object.logo.present? = f.file_field :logo .row .alpha.three.columns - = f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed at the top of the Group profile' - .with-tip{'data-powertip' => 'This image is displayed at the top of the Group profile'} - %a What's this? + = f.label :promo_image, class: 'with-tip', 'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image) + .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image_tip)} + %a + = t 'admin_entreprise_groups_what_s_this' .omega.eight.columns = image_tag @object.promo_image.url if @object.promo_image.present? = f.file_field :promo_image 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 d57333626f..6785d88ada 100644 --- a/app/views/admin/enterprise_groups/_form_primary_details.html.haml +++ b/app/views/admin/enterprise_groups/_form_primary_details.html.haml @@ -1,5 +1,6 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Primary Details'" } } - %legend Primary Details + %legend + = t "admin_entreprise_groups_primary_details" = f.field_container :name do = f.label :name %br/ @@ -11,12 +12,12 @@ = f.text_field :description = f.field_container :on_front_page do - = f.label :on_front_page, 'On front page?' + = f.label :on_front_page, t(:admin_entreprise_groups_on_front_page) %br/ = f.check_box :on_front_page = f.field_container :enterprise_ids do - = f.label :enterprise_ids, 'Enterprises' + = f.label :enterprise_ids, t(:admin_entreprise_groups_entreprise) %br/ = f.collection_select :enterprise_ids, @enterprises, :id, :name, {}, {class: "select2 fullwidth", multiple: true} diff --git a/app/views/admin/enterprise_groups/_form_users.html.haml b/app/views/admin/enterprise_groups/_form_users.html.haml index 0a8a5dd635..dada532af6 100644 --- a/app/views/admin/enterprise_groups/_form_users.html.haml +++ b/app/views/admin/enterprise_groups/_form_users.html.haml @@ -2,9 +2,10 @@ %legend Users .row .three.columns.alpha - =f.label :owner_id, 'Owner' - .with-tip{'data-powertip' => "The primary user responsible for this group."} - %a What's this? + =f.label :owner_id, t(:admin_entreprise_groups_owner) + .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip)} + %a + = t 'admin_entreprise_groups_what_s_this' .eight.columns.omega - if spree_current_user.admin? = f.hidden_field :owner_id, diff --git a/app/views/admin/enterprise_groups/_form_web.html.haml b/app/views/admin/enterprise_groups/_form_web.html.haml index 42638d94c6..4d20f9d2fd 100644 --- a/app/views/admin/enterprise_groups/_form_web.html.haml +++ b/app/views/admin/enterprise_groups/_form_web.html.haml @@ -1,10 +1,11 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Web'" } } - %legend Web Resources + %legend + = t 'admin_entreprise_groups_web' .row .alpha.three.columns = f.label :website .omega.eight.columns - = f.text_field :website, { placeholder: "eg. www.truffles.com"} + = f.text_field :website, { placeholder: t(:admin_entreprise_groups_web_website_placeholder)} .row .alpha.three.columns = f.label :facebook, 'Facebook' @@ -24,4 +25,4 @@ .alpha.three.columns = f.label :twitter .omega.eight.columns - = f.text_field :twitter, { placeholder: "eg. @the_prof" } + = f.text_field :twitter, { placeholder: t(:admin_entreprise_groups_web_twitter) } diff --git a/app/views/admin/enterprise_groups/index.html.haml b/app/views/admin/enterprise_groups/index.html.haml index 13bc19e364..932695201f 100644 --- a/app/views/admin/enterprise_groups/index.html.haml +++ b/app/views/admin/enterprise_groups/index.html.haml @@ -1,5 +1,5 @@ = content_for :page_title do - Enterprise Groups + = t 'admin_entreprise_groups' = content_for :page_actions do %li#new_enterprise_group_link @@ -8,11 +8,15 @@ %table.index#listing_enterprise_groups %thead %tr - %th Name + %th + = t 'admin_entreprise_groups_name' - if spree_current_user.admin? - %th Owner - %th On front page? - %th Enterprises + %th + = t 'admin_entreprise_groups_owner' + %th + = t 'admin_entreprise_groups_on_front_page' + %th + = t 'admin_entreprise_groups_entreprise' %th.actions %tbody diff --git a/app/views/admin/enterprise_relationships/index.html.haml b/app/views/admin/enterprise_relationships/index.html.haml index 40fbdbc415..15d26dcf66 100644 --- a/app/views/admin/enterprise_relationships/index.html.haml +++ b/app/views/admin/enterprise_relationships/index.html.haml @@ -1,5 +1,5 @@ - content_for :page_title do - Enterprise Relationships + = t 'admin_entreprise_relationships' = render 'admin/shared/enterprises_sub_menu' diff --git a/config/locales/en.yml b/config/locales/en.yml index c9df13af92..f6d745ca1d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -658,3 +658,29 @@ Please follow the instructions there to make your enterprise visible on the Open price_graph: "Price graph" included_tax: "Included tax" remove_tax: "Remove tax" + admin_entreprise_relationships: "Enterprise Relationships" + admin_entreprise_groups: "Enterprise Groups" + admin_entreprise_groups_name: "Name" + admin_entreprise_groups_owner: "Owner" + admin_entreprise_groups_on_front_page: "On front page ?" + admin_entreprise_groups_entreprise: "Enterprises" + admin_entreprise_groups_primary_details: "Primary Details" + admin_entreprise_groups_data_powertip: "The primary user responsible for this group." + admin_entreprise_groups_data_powertip_logo: "This is the logo" + admin_entreprise_groups_data_powertip_promo_image: "This image is displayed at the top of the Group profile" + admin_entreprise_groups_data_powertip_promo_image_tip: "This image is displayed at the top of the Group profile" + admin_entreprise_groups_what_s_this: "What's this ?" + admin_entreprise_groups_about: "About" + admin_entreprise_groups_images: "Images" + admin_entreprise_groups_contact: "Contact" + admin_entreprise_groups_contact_phone_placeholder: "eg. 98 7654 3210" + admin_entreprise_groups_contact_address1_placeholder: "eg. 123 High Street" + admin_entreprise_groups_contact_city: "Suburb" + admin_entreprise_groups_contact_city_placeholder: "eg. Northcote" + admin_entreprise_groups_contact_zipcode: "Postcode" + admin_entreprise_groups_contact_zipcode_placeholder: "eg. 3070" + admin_entreprise_groups_contact_state_id: "State" + admin_entreprise_groups_contact_country_id: "Country" + admin_entreprise_groups_web: "Web Resources" + admin_entreprise_groups_web_twitter: "eg. @the_prof" + admin_entreprise_groups_web_website_placeholder: "eg. www.truffles.com" \ No newline at end of file From b58306aad8436e77e07e95f350b9357911390036 Mon Sep 17 00:00:00 2001 From: apprenti Date: Sat, 12 Dec 2015 17:00:41 +0100 Subject: [PATCH 003/215] my first translations --- .../admin/enterprise_fees/index.html.haml | 22 ++++++++----- .../admin/enterprise_roles/index.html.haml | 2 +- app/views/admin/order_cycles/edit.html.haml | 3 +- app/views/admin/order_cycles/index.html.haml | 31 ++++++++++++------- app/views/admin/order_cycles/new.html.haml | 3 +- .../order_cycles/set_coordinator.html.haml | 3 +- .../admin/producer_properties/index.html.haml | 2 +- config/locales/en.yml | 22 +++++++++++-- 8 files changed, 61 insertions(+), 27 deletions(-) diff --git a/app/views/admin/enterprise_fees/index.html.haml b/app/views/admin/enterprise_fees/index.html.haml index 08199d4c4a..42966bd6be 100644 --- a/app/views/admin/enterprise_fees/index.html.haml +++ b/app/views/admin/enterprise_fees/index.html.haml @@ -1,5 +1,5 @@ = content_for :page_title do - Enterprise Fees + =t :Enterprise_Fees = ng_form_for @enterprise_fee_set, :url => main_app.bulk_update_admin_enterprise_fees_path, :html => {'ng-app' => 'enterprise_fees', 'ng-controller' => 'AdminEnterpriseFeesCtrl'} do |enterprise_fee_set_form| = hidden_field_tag 'enterprise_id', @enterprise.id if @enterprise @@ -10,12 +10,18 @@ %table.index#listing_enterprise_fees %thead %tr - %th Enterprise - %th Fee Type - %th Name - %th Tax Category - %th Calculator - %th Calculator values + %th + =t'Enterprise' + %th + =t'fee_type' + %th + =t'name' + %th + =t'tax_category' + %th + =t'calculator' + %th + =t'calculator_values' %th.actions %tbody = enterprise_fee_set_form.ng_fields_for :collection do |f| @@ -30,4 +36,4 @@ %td{'ng-bind-html-unsafe-compiled' => 'enterprise_fee.calculator_settings'} %td.actions{'spree-delete-resource' => "1"} - = enterprise_fee_set_form.submit 'Update' + = enterprise_fee_set_form.submit t(:update) diff --git a/app/views/admin/enterprise_roles/index.html.haml b/app/views/admin/enterprise_roles/index.html.haml index a6e0985a99..45d8115597 100644 --- a/app/views/admin/enterprise_roles/index.html.haml +++ b/app/views/admin/enterprise_roles/index.html.haml @@ -1,5 +1,5 @@ - content_for :page_title do - Roles + =t :roles = render 'admin/shared/users_sub_menu' diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 12dc6238ae..ed9b713d2c 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -4,7 +4,8 @@ = button_to "Notify producers", main_app.notify_producers_admin_order_cycle_path, :id => 'admin_notify_producers', :confirm => 'Are you sure?' -%h1 Edit Order Cycle +%h1 + =t'edit_order_cycle' - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index e39fdde12a..91f267a7d7 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -1,15 +1,15 @@ = content_for :page_title do - Order Cycles + = t :admin_order_cycles = content_for :page_actions do %li#new_order_cycle_link - = button_link_to "New Order Cycle", main_app.new_admin_order_cycle_path, :icon => 'icon-plus', :id => 'admin_new_order_cycle_link' + = button_link_to t(:new_order_cycle), main_app.new_admin_order_cycle_path, :icon => 'icon-plus', :id => 'admin_new_order_cycle_link' - if @show_more %li - = button_link_to "Show less", main_app.admin_order_cycles_path + = button_link_to t(:label_less), main_app.admin_order_cycles_path - else %li - = button_link_to "Show more", main_app.admin_order_cycles_path(params: { show_more: true }) + = button_link_to t(:label_more), main_app.admin_order_cycles_path(params: { show_more: true }) = form_for @order_cycle_set, :url => main_app.bulk_update_admin_order_cycles_path do |f| %table.index#listing_order_cycles @@ -28,14 +28,21 @@ %thead %tr - %th Name - %th Open - %th Close + %th + =t'name' + %th + =t'open' + %th + =t'close' - unless order_cycles_simple_index - %th Supplier - %th Coordinator - %th Distributors - %th Products + %th + =t'supplier' + %th + =t'coordinator' + %th + =t'distributors' + %th + =t'products' %th.actions %th.actions %th.actions @@ -44,4 +51,4 @@ = f.fields_for :collection do |order_cycle_form| = render 'admin/order_cycles/row', order_cycle_form: order_cycle_form - = f.submit 'Update' + = f.submit t(:update) diff --git a/app/views/admin/order_cycles/new.html.haml b/app/views/admin/order_cycles/new.html.haml index 460f2e08ca..4c0bee9e5a 100644 --- a/app/views/admin/order_cycles/new.html.haml +++ b/app/views/admin/order_cycles/new.html.haml @@ -1,4 +1,5 @@ -%h1 New Order Cycle +%h1 + =t'new_order_cycle' - ng_controller = order_cycles_simple_form ? 'AdminSimpleCreateOrderCycleCtrl' : 'AdminCreateOrderCycleCtrl' = admin_inject_order_cycle_instance diff --git a/app/views/admin/order_cycles/set_coordinator.html.haml b/app/views/admin/order_cycles/set_coordinator.html.haml index f2663cbfd2..1a31235f24 100644 --- a/app/views/admin/order_cycles/set_coordinator.html.haml +++ b/app/views/admin/order_cycles/set_coordinator.html.haml @@ -1,4 +1,5 @@ -%h4.text-center Select a coordinator for your order cycle +%h4.text-center + =t'select_a_coordinator_for_your_order_cycle' %br diff --git a/app/views/admin/producer_properties/index.html.haml b/app/views/admin/producer_properties/index.html.haml index d8eac9e816..d898860cb1 100644 --- a/app/views/admin/producer_properties/index.html.haml +++ b/app/views/admin/producer_properties/index.html.haml @@ -6,7 +6,7 @@ - content_for :page_actions do %ul.tollbar.inline-menu %li - = link_to_add_fields 'Add Producer Property', 'tbody#producer_properties', class: 'icon-plus button' + = link_to_add_fields t(:add_producer_property), 'tbody#producer_properties', class: 'icon-plus button' = render 'spree/shared/error_messages', target: @enterprise diff --git a/config/locales/en.yml b/config/locales/en.yml index f6d745ca1d..4198ede74b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -96,7 +96,7 @@ en: label_administration: "Administration" label_admin: "Admin" label_account: "Account" - label_more: "More" + label_more: "Show more" label_less: "Show less" items: "items" @@ -683,4 +683,22 @@ Please follow the instructions there to make your enterprise visible on the Open admin_entreprise_groups_contact_country_id: "Country" admin_entreprise_groups_web: "Web Resources" admin_entreprise_groups_web_twitter: "eg. @the_prof" - admin_entreprise_groups_web_website_placeholder: "eg. www.truffles.com" \ No newline at end of file + admin_entreprise_groups_web_website_placeholder: "eg. www.truffles.com" + admin_order_cycles: "Admin Order Cycles" + open: "Open" + close: "Close" + supplier: "Supplier" + coordinator: "Coordinator" + distributor: "Distributor" + product: "Products" + enterprise_fees: "Enterprise Fees" + fee_type: "Fee Type" + tax_category: "Tax Category" + calculator: "Calculator" + calculator_values: "Calculator values" + new_order_cycles: "New Order Cycles" + select_a_coordinator_for_your_order_cycle: "select a coordinator for your order cycle" + edit_order_cycle: "Edit Order Cycle" + roles: "Roles" + update: "Update" + add_producer_property: "Add producer property" From 8fa293f561c602552b8dd6a6984e7d60900925e5 Mon Sep 17 00:00:00 2001 From: julienFR88 Date: Sat, 12 Dec 2015 17:25:28 +0100 Subject: [PATCH 004/215] my admin interface translations --- .../edit.html.haml | 50 +++++++++++-------- app/views/admin/customers/index.html.haml | 12 +++-- .../variant_overrides/_actions.html.haml | 2 +- .../admin/variant_overrides/_header.html.haml | 2 +- .../variant_overrides/_hub_choice.html.haml | 4 +- .../variant_overrides/_products.html.haml | 8 +-- config/locales/en.yml | 29 +++++++++++ 7 files changed, 75 insertions(+), 32 deletions(-) diff --git a/app/views/admin/accounts_and_billing_settings/edit.html.haml b/app/views/admin/accounts_and_billing_settings/edit.html.haml index 71fac1fd28..434ad6884c 100644 --- a/app/views/admin/accounts_and_billing_settings/edit.html.haml +++ b/app/views/admin/accounts_and_billing_settings/edit.html.haml @@ -8,9 +8,10 @@ -# - month_options = (0...12).map { |i| Time.zone.now.beginning_of_month - i.months }.map{ |t| [t.strftime("%b %Y"), t.strftime("%b %Y %z")]} %fieldset.no-border-bottom - %legend Settings + %legend + =t :admin_settings = form_for @settings, as: :settings, url: main_app.admin_accounts_and_billing_settings_path, :method => :put do |f| - .row{ ng: { app: 'admin.accounts_and_billing_settings' } } + .row{ ng: { app: t(:admin_accounts_and_billing) } } .twelve.columns.alpha.omega .field = f.label :accounts_distributor_id, t(:accounts_administration_distributor) @@ -23,26 +24,32 @@ .row .six.columns.alpha %fieldset.no-border-bottom - %legend Update Invoices + %legend + =t :update_invoice = f.check_box :auto_update_invoices - = f.label :auto_update_invoices, "Auto-update invoices nightly at 1:00am" + = f.label :auto_update_invoices, + t(:auto_finalise_invoices) .six.columns.omega %fieldset.no-border-bottom - %legend Finalise Invoices + %legend + =t :finalise_invoice = f.check_box :auto_finalize_invoices - = f.label :auto_finalize_invoices, "Auto-finalise invoices monthly on the 2nd at 1:30am" + = f.label :auto_finalize_invoices, + t(:auto_update_invoices) .row .twelve.columns.alpha.omega.form-buttons{"data-hook" => "buttons"} = button t(:update), 'icon-refresh', value: "update" %fieldset.no-border-bottom - %legend Manually Run Tasks + %legend + =t :manually_run_task .row .six.columns.alpha.step.text-center .form-buttons{"data-hook" => "buttons"} - =link_to_with_icon "icon-undo", "Update User Invoices", + =link_to_with_icon "icon-undo", + t(:update_user_invoices) , main_app.start_job_admin_accounts_and_billing_settings_path(job: { name: "update_account_invoices" }), class: "button fullwidth" @@ -51,22 +58,22 @@ - if @update_account_invoices_job %p.text-center - if @update_account_invoices_job.run_at < Time.zone.now - %strong In Progress + =t :in_progress %br - Started at: + =t :started_at - else - %strong Queued + %strong + =t :queued %br - Scheduled for: + =t :Scheduled_for = @update_account_invoices_job.run_at - else %p.explanation - Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night. - + =t :update_user_invoice_explained .six.columns.omega.step.text-center .form-buttons{"data-hook" => "buttons"} - =link_to_with_icon "icon-ok-sign", "Finalise User Invoices", + =link_to_with_icon "icon-ok-sign", t(:finalise_user_invoice ), main_app.start_job_admin_accounts_and_billing_settings_path(job: { name: "finalize_account_invoices" }), class: "button fullwidth" @@ -75,14 +82,17 @@ - if @finalize_account_invoices_job %p.text-center - if @finalize_account_invoices_job.run_at < Time.zone.now - %strong In Progress + %strong + =t :in_progress %br - Started at: + =t :started_at - else - %strong Queued + %strong + =t :queued %br - Scheduled for: + =t :scheduled_for = @finalize_account_invoices_job.run_at - else %p.explanation - Use this button to finalize all invoices in the system for the previous calendar month. This task can be set up to run automatically once a month. + =t :finalise_user_invoice_explained + diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index 66790a34df..170ace343e 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -1,12 +1,14 @@ - content_for :page_title do - %h1.page-title Customers + %h1.page-title + =t :customers = admin_inject_shops %div{ ng: { app: 'admin.customers', controller: 'customersCtrl' } } .row{ ng: { hide: "loaded() && filteredCustomers.length > 0" } } .five.columns.alpha - %h3 Please select a Hub: + %h3 + =t :please_select_hub .four.columns %select.select2.fullwidth#shop_id{ 'ng-model' => 'shop.id', name: 'shop_id', 'ng-options' => 'shop.id as shop.name for shop in shops' } .seven.columns.omega   @@ -34,9 +36,11 @@ .row{ 'ng-if' => 'shop && !loaded()' } .sixteen.columns.alpha#loading %img.spinner{ src: "/assets/spinning-circles.svg" } - %h1 LOADING CUSTOMERS + %h1 + =t :loading_customers .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded() && filteredCustomers.length == 0'} - %h1#no_results No customers found. + %h1#no_results + =t :no_customers_found .row{ ng: { show: "loaded() && filteredCustomers.length > 0" } } diff --git a/app/views/admin/variant_overrides/_actions.html.haml b/app/views/admin/variant_overrides/_actions.html.haml index 0ae6f8b96b..8fea8b6838 100644 --- a/app/views/admin/variant_overrides/_actions.html.haml +++ b/app/views/admin/variant_overrides/_actions.html.haml @@ -1,4 +1,4 @@ .row - %input.four.columns.alpha{type: 'button', value: 'Save Changes', 'ng-click' => 'update()'} + %input.four.columns.alpha{type: 'button', value: t(:save_changes), 'ng-click' => 'update()'} .twelve.columns.omega = render 'spree/admin/shared/status_message' diff --git a/app/views/admin/variant_overrides/_header.html.haml b/app/views/admin/variant_overrides/_header.html.haml index 7b4a38db47..06334855b5 100644 --- a/app/views/admin/variant_overrides/_header.html.haml +++ b/app/views/admin/variant_overrides/_header.html.haml @@ -1,4 +1,4 @@ - content_for :page_title do - Override Product Details + =t :override_product_details = render :partial => 'spree/admin/shared/product_sub_menu' diff --git a/app/views/admin/variant_overrides/_hub_choice.html.haml b/app/views/admin/variant_overrides/_hub_choice.html.haml index aa0f7ab738..c7b19458e1 100644 --- a/app/views/admin/variant_overrides/_hub_choice.html.haml +++ b/app/views/admin/variant_overrides/_hub_choice.html.haml @@ -1,7 +1,7 @@ .row .two.columns.alpha - Hub + =t :hub .four.columns %select.select2.fullwidth#hub_id{ 'ng-model' => 'hub_id', name: 'hub_id', 'ng-options' => 'hub.id as hub.name for hub in hubs' } .ten.columns.omega - %input{ type: 'button', value: 'Go', 'ng-click' => 'selectHub()' } + %input{ type: 'button', value: t(:go), 'ng-click' => 'selectHub()' } diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index cf11e8ac5d..4ee3644798 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,10 +1,10 @@ %table.index.bulk{ng: {show: 'hub'}} %thead %tr - %th Producer - %th Product - %th Price - %th On hand + %th t(:producer) + %th t(:product) + %th t(:price) + %th t(:on_hand) %tbody{ng: {repeat: 'product in products | hubPermissions:hubPermissions:hub.id'}} = render 'admin/variant_overrides/products_product' = render 'admin/variant_overrides/products_variants' diff --git a/config/locales/en.yml b/config/locales/en.yml index 4198ede74b..1d9f7a6120 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -702,3 +702,32 @@ Please follow the instructions there to make your enterprise visible on the Open roles: "Roles" update: "Update" add_producer_property: "Add producer property" + admin_settings: "Settings" + update_invoice: "Update Invoices" + finalise_invoice: "Finalise Invoices" + finalise_user_invoice: "Finalise User Invoice" + finalise_user_invoice_explained: "Use this button to finalize all invoices in the system for the previous calendar month. This task can be set up to run automatically once a month." + manually_run_task: "Manually Run Task " + update_user_invoices: "Update User Invoices" + update_user_invoice_explained: "Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night." + auto_finalise_invoices: "Auto-finalise invoices monthly on the 2nd at 1:30am" + auto_update_invoices: "Auto-update invoices nightly at 1:00am" + in_progress: "In Progress" + started_at: "Started at" + queued: "Queued" + scheduled_for: "Scheduled for" + customers: "Customers" + please_select_hub: "Please select a Hub" + loading_customers: "Loading Customers" + no_customers_found: "No customers found" + go: "Go" + hub: "Hub" + accounts_administration_distributor: "accounts administration distributor" + override_product_details: "Override Product Details" + admin_accounts_and_billing: "admin.accounts_and_billing_settings" + producer: "Producer" + product: "Product" + price: "Price" + on_hand: "On hand" + save_changes: "Save changes" + update_action: "update()" From d2ec4fcf88eb2478e7d7728a6ed188ea4a38c39c Mon Sep 17 00:00:00 2001 From: Edem Date: Sun, 13 Dec 2015 17:06:49 +0100 Subject: [PATCH 005/215] Everything string translated in admin/entreprise_relationships --- .../_enterprise_relationship.html.haml | 3 ++- app/views/admin/enterprise_relationships/_form.html.haml | 6 +++--- .../admin/enterprise_relationships/_search_input.html.haml | 2 +- config/locales/en.yml | 7 ++++++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml index 996bc487d8..b4e865f083 100644 --- a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml +++ b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml @@ -1,6 +1,7 @@ %tr{"ng-repeat" => "enterprise_relationship in EnterpriseRelationships.enterprise_relationships | keywords:query"} %td {{ enterprise_relationship.parent_name }} - %td permits + %td + = t 'admin_entreprise_relationships_permits' %td {{ enterprise_relationship.child_name }} %td %ul diff --git a/app/views/admin/enterprise_relationships/_form.html.haml b/app/views/admin/enterprise_relationships/_form.html.haml index 433f9f4ca0..300b096424 100644 --- a/app/views/admin/enterprise_relationships/_form.html.haml +++ b/app/views/admin/enterprise_relationships/_form.html.haml @@ -3,17 +3,17 @@ %select{name: "enterprise_relationship_parent_id", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.my_enterprises"} %td - permits + = t 'admin_entreprise_relationships_permits' %td %select{name: "enterprise_relationship_child_id", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.all_enterprises"} %td %label %input{type: "checkbox", ng: {checked: "allPermissionsChecked()", click: "checkAllPermissions()"}} - Everything + = t 'admin_entreprise_relationships_everything' %div{"ng-repeat" => "permission in EnterpriseRelationships.all_permissions"} %label %input{type: "checkbox", "ng-model" => "permissions[permission]"} to {{ EnterpriseRelationships.permission_presentation(permission) }} %td.actions - %input{type: "button", value: "Create", "ng-click" => "create()"} + %input{type: "button", value: t(:admin_entreprise_relationships_button_create), "ng-click" => "create()"} .errors {{ EnterpriseRelationships.create_errors }} diff --git a/app/views/admin/enterprise_relationships/_search_input.html.haml b/app/views/admin/enterprise_relationships/_search_input.html.haml index b8bcbc62c6..350089dc4f 100644 --- a/app/views/admin/enterprise_relationships/_search_input.html.haml +++ b/app/views/admin/enterprise_relationships/_search_input.html.haml @@ -1,4 +1,4 @@ -%input.search{"ng-model" => "query", "placeholder" => "Search"} +%input.search{"ng-model" => "query", "placeholder" => t(:admin_entreprise_relationships_seach_placeholder)} %label{ng: {repeat: "permission in EnterpriseRelationships.all_permissions"}} %input{type: "checkbox", ng: {click: "$parent.query = toggleKeyword($parent.query, permission)"}} diff --git a/config/locales/en.yml b/config/locales/en.yml index 1d9f7a6120..7e1274739c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -659,6 +659,10 @@ Please follow the instructions there to make your enterprise visible on the Open included_tax: "Included tax" remove_tax: "Remove tax" admin_entreprise_relationships: "Enterprise Relationships" + admin_entreprise_relationships_everything: "Everything" + admin_entreprise_relationships_permits: "permits" + admin_entreprise_relationships_seach_placeholder: "Search" + admin_entreprise_relationships_button_create: "Create" admin_entreprise_groups: "Enterprise Groups" admin_entreprise_groups_name: "Name" admin_entreprise_groups_owner: "Owner" @@ -669,7 +673,6 @@ Please follow the instructions there to make your enterprise visible on the Open admin_entreprise_groups_data_powertip_logo: "This is the logo" admin_entreprise_groups_data_powertip_promo_image: "This image is displayed at the top of the Group profile" admin_entreprise_groups_data_powertip_promo_image_tip: "This image is displayed at the top of the Group profile" - admin_entreprise_groups_what_s_this: "What's this ?" admin_entreprise_groups_about: "About" admin_entreprise_groups_images: "Images" admin_entreprise_groups_contact: "Contact" @@ -684,6 +687,7 @@ Please follow the instructions there to make your enterprise visible on the Open admin_entreprise_groups_web: "Web Resources" admin_entreprise_groups_web_twitter: "eg. @the_prof" admin_entreprise_groups_web_website_placeholder: "eg. www.truffles.com" +<<<<<<< HEAD admin_order_cycles: "Admin Order Cycles" open: "Open" close: "Close" @@ -731,3 +735,4 @@ Please follow the instructions there to make your enterprise visible on the Open on_hand: "On hand" save_changes: "Save changes" update_action: "update()" + admin_entreprise_groups_what_s_this: "What's this ?" From 8ccf527e01dd0366469d30298f948b23fe71fc56 Mon Sep 17 00:00:00 2001 From: Edem Date: Wed, 16 Dec 2015 19:00:25 +0100 Subject: [PATCH 006/215] all strings in app/views/spree/admin/overview/* translated! --- .../overview/_enterprises_footer.html.haml | 2 +- .../overview/_enterprises_header.html.haml | 8 ++-- .../overview/_enterprises_hubs_tab.html.haml | 9 ++-- .../overview/_enterprises_none.html.haml | 5 ++- .../_enterprises_producers_tab.html.haml | 12 ++++-- .../overview/_enterprises_tabs.html.haml | 6 ++- .../admin/overview/_order_cycles.html.haml | 15 ++++--- .../spree/admin/overview/_products.html.haml | 15 ++++--- .../multi_enterprise_dashboard.html.haml | 3 +- .../single_enterprise_dashboard.html.haml | 38 +++++++++-------- config/locales/en.yml | 42 ++++++++++++++++++- 11 files changed, 107 insertions(+), 48 deletions(-) diff --git a/app/views/spree/admin/overview/_enterprises_footer.html.haml b/app/views/spree/admin/overview/_enterprises_footer.html.haml index c61add38f5..c704dfef64 100644 --- a/app/views/spree/admin/overview/_enterprises_footer.html.haml +++ b/app/views/spree/admin/overview/_enterprises_footer.html.haml @@ -1,3 +1,3 @@ %a.sixteen.columns.alpha.button.bottom.blue{ href: "#{main_app.admin_enterprises_path}" } - MANAGE MY ENTERPRISES + = t "spree_admin_overview_enterprises_footer" %span.icon-arrow-right diff --git a/app/views/spree/admin/overview/_enterprises_header.html.haml b/app/views/spree/admin/overview/_enterprises_header.html.haml index fcc7d269f2..6ed4ae8d65 100644 --- a/app/views/spree/admin/overview/_enterprises_header.html.haml +++ b/app/views/spree/admin/overview/_enterprises_header.html.haml @@ -1,8 +1,10 @@ %div.header.sixteen.columns.alpha{ :class => "#{@enterprises.count > 0 ? "" : "red"}"} - %h3.thirteen.columns.alpha My Enterprises + %h3.thirteen.columns.alpha + = t "spree_admin_overview_enterprises_header" - if @enterprises.any? - if spree_current_user.can_own_more_enterprises? %a.three.columns.omega.icon-plus.button.blue.white-bottom{ href: "#{main_app.new_admin_enterprise_path}" } - CREATE NEW + = t "spree_admin_enterprises_create_new" - else - %a.with-tip{ title: "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } What's this? + %a.with-tip{ title: "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } + = t "admin_enterprise_groups_what_s_this" diff --git a/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml b/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml index cb177f9fb3..91deb6f1cc 100644 --- a/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml +++ b/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml @@ -1,12 +1,15 @@ %div.hubs_tab{ ng: { show: "activeTab == 'hubs'"} } %div.sixteen.columns.alpha.list-title - %span.five.columns.alpha Name + %span.five.columns.alpha + = t "spree_admin_enterprises_hubs_name" - if can? :admin, Spree::PaymentMethod %span.centered.three.columns Payment Methods - if can? :admin, Spree::ShippingMethod - %span.centered.three.columns Shipping Methods + %span.centered.three.columns + = t "spree_admin_enterprises_shipping_methods" - if can? :admin, EnterpriseFee - %span.centered.three.columns Enterprise Fees + %span.centered.three.columns + = t "spree_admin_enterprises_fees" %div.sixteen.columns.alpha.list - @enterprises.is_distributor.each do |enterprise| %a.sixteen.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{main_app.edit_admin_enterprise_path(enterprise)}" } diff --git a/app/views/spree/admin/overview/_enterprises_none.html.haml b/app/views/spree/admin/overview/_enterprises_none.html.haml index c1428a6863..72cd90df1a 100644 --- a/app/views/spree/admin/overview/_enterprises_none.html.haml +++ b/app/views/spree/admin/overview/_enterprises_none.html.haml @@ -1,7 +1,8 @@ %div.sixteen.columns.alpha.list-item.red - %span.text.fifteen.columns.alpha You don't have any enterprises yet. + %span.text.fifteen.columns.alpha + = t "spree_admin_enterprises_none_text" %span.one.columns.omega %span.icon-remove-sign %a.sixteen.columns.alpha.button.bottom.red{ href: "#{main_app.new_admin_enterprise_path}" } - CREATE A NEW ENTERPRISE + = t "spree_admin_enterprises_none_create_a_new_enterprise" %span.icon-arrow-right diff --git a/app/views/spree/admin/overview/_enterprises_producers_tab.html.haml b/app/views/spree/admin/overview/_enterprises_producers_tab.html.haml index d5cad103b7..7ff6c8b911 100644 --- a/app/views/spree/admin/overview/_enterprises_producers_tab.html.haml +++ b/app/views/spree/admin/overview/_enterprises_producers_tab.html.haml @@ -1,11 +1,15 @@ %div.producers_tab{ ng: { show: "activeTab == 'producers'"} } %div.list-title.sixteen.columns.alpha - %span.five.columns.alpha Name + %span.five.columns.alpha + = t "spree_admin_enterprises_producers_name" - if can? :admin, Spree::Product - %span.centered.three.columns Total Products - %span.centered.three.columns Active Products + %span.centered.three.columns + = t "spree_admin_enterprises_producers_total_products" + %span.centered.three.columns + = t "spree_admin_enterprises_producers_active_products" - if can? :admin, OrderCycle - %span.centered.three.columns Products in OCs + %span.centered.three.columns + = t "spree_admin_enterprises_producers_order_cycles" %div.sixteen.columns.alpha.list - @enterprises.is_primary_producer.each do |enterprise| %a.sixteen.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{main_app.edit_admin_enterprise_path(enterprise)}" } diff --git a/app/views/spree/admin/overview/_enterprises_tabs.html.haml b/app/views/spree/admin/overview/_enterprises_tabs.html.haml index f6e5413786..1124c90f30 100644 --- a/app/views/spree/admin/overview/_enterprises_tabs.html.haml +++ b/app/views/spree/admin/overview/_enterprises_tabs.html.haml @@ -1,3 +1,5 @@ %div.sixteen.columns.alpha.tabs - %div.dashboard_tab.eight.columns.alpha.blue{ ng: { class: "{selected: activeTab == 'hubs'}", click: "activeTab = 'hubs'" } } HUBS - %div.dashboard_tab.eight.columns.omega.blue{ ng: { class: "{selected: activeTab == 'producers'}", click: "activeTab = 'producers'" } } PRODUCERS + %div.dashboard_tab.eight.columns.alpha.blue{ ng: { class: "{selected: activeTab == 'hubs'}", click: "activeTab = 'hubs'" } } + = t "spree_admin_enterprises_tabs_hubs" + %div.dashboard_tab.eight.columns.omega.blue{ ng: { class: "{selected: activeTab == 'producers'}", click: "activeTab = 'producers'" } } + = t "spree_admin_enterprises_tabs_producers" diff --git a/app/views/spree/admin/overview/_order_cycles.html.haml b/app/views/spree/admin/overview/_order_cycles.html.haml index c1b7f90276..b39d8a706f 100644 --- a/app/views/spree/admin/overview/_order_cycles.html.haml +++ b/app/views/spree/admin/overview/_order_cycles.html.haml @@ -1,11 +1,13 @@ %div.dashboard_item.seven.columns.omega#order_cycles %div.header.seven.columns.alpha{ :class => "#{@order_cycle_count > 0 ? "" : "orange"}"} - %h3.four.columns.alpha Order Cycles + %h3.four.columns.alpha + = t "spree_admin_order_cycles" - if @order_cycle_count > 0 %a.three.columns.omega.icon-plus.button.blue{ href: "#{main_app.new_admin_order_cycle_path}" } - CREATE NEW + = t "spree_admin_enterprises_create_new" - else - %a.with-tip{ title: "Order cycles determine when and where your products are available to customers." } What's this? + %a.with-tip{ title: t(:spree_admin_order_cycles_tip) } + = t "admin_entreprise_groups_what_s_this" %div.seven.columns.alpha.list - if @order_cycle_count > 0 %div.seven.columns.alpha.list-item @@ -14,13 +16,14 @@ %span.one.column.omega %span.icon-ok-sign %a.seven.columns.alpha.button.bottom.blue{ href: "#{main_app.admin_order_cycles_path}" } - MANAGE ORDER CYCLES + = t "spree_admin_enterprises_producers_manage_order_cycles" %span.icon-arrow-right - else %div.seven.columns.alpha.list-item.orange - %span.six.columns.alpha You don't have any active order cycles. + %span.six.columns.alpha + = t "spree_admin_enterprises_producers_orders_cycle_text" %span.one.column.omega %span.icon-warning-sign %a.seven.columns.alpha.button.bottom.orange{ href: "#{main_app.admin_order_cycles_path}" } - MANAGE ORDER CYCLES + = t "spree_admin_enterprises_producers_manage_order_cycles" %span.icon-arrow-right \ No newline at end of file diff --git a/app/views/spree/admin/overview/_products.html.haml b/app/views/spree/admin/overview/_products.html.haml index 988e779398..0b62e945d0 100644 --- a/app/views/spree/admin/overview/_products.html.haml +++ b/app/views/spree/admin/overview/_products.html.haml @@ -1,11 +1,13 @@ %div.dashboard_item.seven.columns.alpha#products %div.header.seven.columns.alpha{ :class => "#{@product_count > 0 ? "" : "red"}"} - %h3.four.columns.alpha Products + %h3.four.columns.alpha + = t "products" - if @product_count > 0 %a.three.columns.omega.icon-plus.button.blue{ href: "#{new_admin_product_path}" } - CREATE NEW + = t "spree_admin_enterprises_create_new" - else - %a.with-tip{ title: "The products that you sell through the Open Food Network." } What's this? + %a.with-tip{ title: "The products that you sell through the Open Food Network." } + = t "admin_entreprise_groups_what_s_this" %div.seven.columns.alpha.list - if @product_count > 0 %div.seven.columns.alpha.list-item @@ -14,13 +16,14 @@ %span.one.column.omega %span.icon-ok-sign %a.seven.columns.alpha.button.bottom.blue{ href: "#{bulk_edit_admin_products_path}" } - MANAGE PRODUCTS + = t "spree_admin_enterprises_producers_manage_products" %span.icon-arrow-right - else %div.seven.columns.alpha.list-item.red - %span.six.columns.alpha You don't have any active products. + %span.six.columns.alpha + = t "spree_admin_enterprises_any_active_products_text" %span.one.column.omega %span.icon-remove-sign %a.seven.columns.alpha.button.bottom.red{ href: "#{new_admin_product_path}" } - CREATE A NEW PRODUCT + = t "spree_admin_enterprises_create_new_product" %span.icon-arrow-right \ No newline at end of file diff --git a/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml index 420196d3ce..6e1b43fce2 100644 --- a/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml @@ -2,7 +2,8 @@ = render 'admin/shared/user_guide_link' -%h1{ :style => 'margin-bottom: 30px'} Dashboard +%h1{ :style => 'margin-bottom: 30px'} + = t "dashbord" - if @enterprises.unconfirmed.any? diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml index 3998f736de..119c907678 100644 --- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml @@ -19,7 +19,7 @@ } #package_button %button#toggle_type{ onClick: 'toggleType()' } - Change Package + = t "change_package" %i.icon-chevron-down @@ -29,17 +29,17 @@ - if @enterprise.confirmed_at.nil? .alert-box - Please confirm the email address for + = t "spree_admin_single_enterprise_alert_mail_confirmation" %strong= "#{@enterprise.name}." - We've sent an email to + t(:spree_admin_single_enterprise_alert_mail_sent) %strong= "#{@enterprise.email}." - = link_to('Resend', main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: @enterprise.email } ), method: :post) + = link_to(t('resend'), main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: @enterprise.email } ), method: :post) %a.close{ href: "#" } × - if !@enterprise.visible .alert-box - %strong Hint: - To allow people to find you, turn on your visibility under - %strong= "Manage #{@enterprise.name}." + %strong + = t "spree_admin_single_enterprise_hint" + %strong= "#{t('manage')} #{@enterprise.name}." %a.close{ href: "#" } × .row @@ -47,14 +47,15 @@ .header %h3 %span.icon-map-marker - Your profile live - %p on the Open Food Network map + = t "your_profil_live" + %p + = t "on_ofn_map" .list /-# Can we pass an anchor here to zoom to our enterprise? %a.button.bottom{href: main_app.map_path, target: '_blank'} - See + = t "see" = @enterprise.name - live + = t "live" %span.icon-arrow-right .two.columns   @@ -63,11 +64,12 @@ .header %h3 %span.icon-edit - Edit profile details - %p Change your profile description, images, etc. + = t "edit_profile_details" + %p + = t "edit_profile_details_etc" .list %a.button.bottom{href: main_app.edit_admin_enterprise_path(@enterprise)} - Manage + = t "manage" = @enterprise.name %span.icon-arrow-right @@ -77,10 +79,10 @@ .header %h3 %span.icon-th-large - Add & manage products + = t 'add_and_manage_products' .list %a.button.bottom{href: bulk_edit_admin_products_path} - Manage products + = t 'manage_products' %span.icon-arrow-right .two.columns @@ -91,8 +93,8 @@ .header %h3 %span.icon-shopping-cart - Add & manage order cycles + = t 'add_and_manage_order_cycles' .list %a.button.bottom{href: main_app.admin_order_cycles_path} - Manage order cycles + = t 'manage_order_cycles' %span.icon-arrow-right diff --git a/config/locales/en.yml b/config/locales/en.yml index 7e1274739c..1ccd46d5d7 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -687,7 +687,6 @@ Please follow the instructions there to make your enterprise visible on the Open admin_entreprise_groups_web: "Web Resources" admin_entreprise_groups_web_twitter: "eg. @the_prof" admin_entreprise_groups_web_website_placeholder: "eg. www.truffles.com" -<<<<<<< HEAD admin_order_cycles: "Admin Order Cycles" open: "Open" close: "Close" @@ -734,5 +733,44 @@ Please follow the instructions there to make your enterprise visible on the Open price: "Price" on_hand: "On hand" save_changes: "Save changes" - update_action: "update()" + update_action: "update()" #FIXME admin_entreprise_groups_what_s_this: "What's this ?" + spree_admin_overview_enterprises_header: "My Enterprises" + spree_admin_overview_enterprises_footer: "MANAGE MY ENTERPRISES" + spree_admin_enterprises_hubs_name: "Name" + spree_admin_enterprises_create_new: "CREATE NEW" + spree_admin_enterprises_shipping_methods: "Shipping Methods" + spree_admin_enterprises_fees: "Enterprise Fees" + spree_admin_enterprises_none_create_a_new_enterprise: "CREATE A NEW ENTERPRISE" + spree_admin_enterprises_none_text: "You don't have any enterprises yet" + spree_admin_enterprises_producers_name: "Name" + spree_admin_enterprises_producers_total_products: "Total Products" + spree_admin_enterprises_producers_active_products: "Active Products" + spree_admin_enterprises_producers_order_cycles: "Products in OCs" + spree_admin_enterprises_producers_order_cycles_title: "" + spree_admin_enterprises_tabs_hubs: "HUBS" + spree_admin_enterprises_tabs_producers: "PRODUCERS" + spree_admin_enterprises_producers_manage_order_cycles: "MANAGE ORDER CYCLES" + spree_admin_enterprises_producers_manage_products: "MANAGE PRODUCTS" + spree_admin_enterprises_producers_orders_cycle_text: "You don't have any active order cycles." + spree_admin_enterprises_any_active_products_text: "You don't have any active products." + spree_admin_enterprises_create_new_product: "CREATE A NEW PRODUCT" + spree_admin_order_cycles: "Order Cycles" + spree_admin_order_cycles_tip: "Order cycles determine when and where your products are available to customers." + dashbord: "Dashboard" + spree_admin_single_enterprise_alert_mail_confirmation: "Please confirm the email address for" + spree_admin_single_enterprise_alert_mail_sent: "We've sent an email to" + change_package: "Change Package" + spree_admin_single_enterprise_hint: "Hint: To allow people to find you, turn on your visibility under" + your_profil_live: "Your profile live" + on_ofn_map: "on the Open Food Network map" + see: "See" + live: "live" + manage: "Manage" + resend: "Resend" + add_and_manage_products: "Add & manage products" + add_and_manage_order_cycles: "Add & manage order cycles" + manage_order_cycles: "Manage order cycles" + manage_products: "Manage products" + edit_profile_details: "Edit profile details" + edit_profile_details_etc: "Change your profile description, images, etc." From 6cd7fbb455d3fb0f866dd5178ff4dfef7682016f Mon Sep 17 00:00:00 2001 From: Edem Date: Thu, 17 Dec 2015 13:53:06 +0100 Subject: [PATCH 007/215] all string translated in admin bulk orders management --- .../admin/orders/bulk_management.html.haml | 69 ++++++++++++------- config/locales/en.yml | 16 +++++ 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index dbcf9c97fd..cc7c0f171a 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -1,6 +1,8 @@ - content_for :page_title do - %h1.page-title Bulk Order Management - %a.with-tip{ 'data-powertip' => "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." } What's this? + %h1.page-title + = t "bulk_order_management" + %a.with-tip{ 'data-powertip' => "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." } + = t "admin_entreprise_groups_what_s_this" = render :partial => 'spree/admin/shared/order_sub_menu' @@ -8,16 +10,19 @@ %save-bar{ save: "submit()", saving: 'saving', dirty: "bulk_order_form.$dirty" } .filters{ :class => "sixteen columns alpha" } .date_filter{ :class => "two columns alpha" } - %label{ :for => 'start_date_filter' }Start Date + %label{ :for => 'start_date_filter' } + = t "start_date" %br %input{ :class => "two columns alpha", :type => "text", :id => 'start_date_filter', 'ng-model' => 'startDate', 'datepicker' => "startDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()' } .date_filter{ :class => "two columns" } - %label{ :for => 'end_date_filter' }End Date + %label{ :for => 'end_date_filter' } + = t "end_date" %br %input{ :class => "two columns alpha", :type => "text", :id => 'end_date_filter', 'ng-model' => 'endDate', 'datepicker' => "endDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()' } .one.column   .filter_select{ :class => "three columns" } - %label{ :for => 'supplier_filter' }Producer + %label{ :for => 'supplier_filter' } + = t "producer" %br %select{ :class => "three columns alpha", :id => 'supplier_filter', 'select2-min-search' => 5, 'ng-model' => 'supplierFilter', 'ng-options' => 's.id as s.name for s in suppliers' } .filter_select{ :class => "three columns" } @@ -25,7 +30,8 @@ %br %select{ :class => "three columns alpha", :id => 'distributor_filter', 'select2-min-search' => 5, 'ng-model' => 'distributorFilter', 'ng-options' => 'd.id as d.name for d in distributors'} .filter_select{ :class => "three columns" } - %label{ :for => 'order_cycle_filter' }Order Cycle + %label{ :for => 'order_cycle_filter' } + = t "order_cycle" %br %select{ :class => "three columns alpha", :id => 'order_cycle_filter', 'select2-min-search' => 5, 'ng-model' => 'orderCycleFilter', 'ng-options' => 'oc.id as oc.name for oc in orderCycles', 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()'} .filter_clear{ :class => "two columns omega" } @@ -48,28 +54,33 @@ .row .one.column.alpha   .two.columns - %span.two.columns Group Buy Unit Size + %span.two.columns + = t "group_buy_unit_size" %span.two.columns {{ formattedValueWithUnitName( selectedUnitsProduct.group_buy_unit_size, selectedUnitsProduct, selectedUnitsVariant ) }} .one.column   .two.columns - %span.two.columns Total Quantity Ordered + %span.two.columns + = t "total_qtt_ordered" %span.two.columns {{ formattedValueWithUnitName( sumUnitValues(), selectedUnitsProduct, selectedUnitsVariant ) }} .one.column   .two.columns - %span.two.columns Max Quantity Ordered + %span.two.columns + = t "max_qtt_ordered" %span.two.columns {{ formattedValueWithUnitName( sumMaxUnitValues(), selectedUnitsProduct, selectedUnitsVariant ) }} .one.column   .two.columns - %span.two.columns Current Fulfilled Units + %span.two.columns + = t "current_fulfilled_units" %span.two.columns {{ fulfilled(sumUnitValues()) }} .one.column   .two.columns - %span.two.columns Max Fulfilled Units + %span.two.columns + = t "max_fulfilled_units" %span.two.columns {{ fulfilled(sumMaxUnitValues()) }} .one.column.omega   %div{ :class => "eight columns alpha", 'ng-hide' => 'allFinalWeightVolumesPresent()' } %span{ :class => "eight columns alpha", style: 'color:red' } - WARNING: Some variants do not have a unit value + = t "bulk_management_warning" %hr{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px" } %div{ 'ng-hide' => 'RequestMonitor.loading || lineItems.length == 0' } .controls{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px;" } @@ -77,7 +88,8 @@ %input{ :class => "fullwidth", :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } %div{ :class => "three columns" } %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "bulk_actions_dropdown", 'ofn-drop-down' => true } - %span{ :class => 'icon-check' }   Actions + %span{ :class => 'icon-check' } + = t "action" %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } %div.menu{ 'ng-show' => "expanded" } %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "action in bulkActions", 'ng-click' => "$eval(action.callback)(filteredLineItems)", 'ofn-close-on-click' => true } @@ -95,7 +107,8 @@ %img.spinner{ src: "/assets/spinning-circles.svg" } %h1 LOADING ORDERS %div{ :class => "sixteen columns alpha", 'ng-show' => '!RequestMonitor.loading && filteredLineItems.length == 0'} - %h1#no_results No orders found. + %h1#no_results + = t "no_orders_found" %div{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } %form{ name: 'bulk_order_form' } %table.index#listing_orders.bulk{ :class => "sixteen columns alpha" } @@ -104,13 +117,17 @@ %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. + %a{ :href => '', 'ng-click' => "predicate = 'order.number'; reverse = !reverse" } + = t "order_no" %th.full_name{ 'ng-show' => 'columns.full_name.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.full_name'; reverse = !reverse" } Name + %a{ :href => '', 'ng-click' => "predicate = 'order.full_name'; reverse = !reverse" } + = t "name" %th.email{ 'ng-show' => 'columns.email.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.email'; reverse = !reverse" } Email + %a{ :href => '', 'ng-click' => "predicate = 'order.email'; reverse = !reverse" } + = t "email" %th.phone{ 'ng-show' => 'columns.phone.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } Phone + %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } + = t "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' } @@ -121,13 +138,17 @@ %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.full_name'; reverse = !reverse" } Product: Unit - %th.quantity{ 'ng-show' => 'columns.quantity.visible' } Quantity - %th.max{ 'ng-show' => 'columns.max.visible' } Max - %th.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' } Weight/Volume - %th.price{ 'ng-show' => 'columns.price.visible' } Price + %th.quantity{ 'ng-show' => 'columns.quantity.visible' } + = t "products_quantity" + %th.max{ 'ng-show' => 'columns.max.visible' } + = t "shop_variant_quantity_max" + %th.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' } + = t "weight_volume" + %th.price{ 'ng-show' => 'columns.price.visible' } + = t "products_price" %th.actions %th.actions - Ask?  + = t "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 @@ -143,7 +164,7 @@ %td.variant{ 'ng-show' => 'columns.variant.visible' } %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.show-dirty{ :type => 'number', :name => 'quantity', :id => 'quantity', ng: { model: "line_item.quantity", change: "updateOnQuantity(line_item)", required: "true", class: '{"update-error": line_item.errors.quantity}' }, min: 1, step: 1 } + %input.show-dirty{ :type => 'number', :name => 'quantity', :id => 'quantity', ng: { model: "line_item.quantity", change: "updateOnQuantity(line_item)", required: "true", class: '{"update-error": line_item.errors.quantity}' }, min: 1, t(:step): 1 } %span.error{ ng: { bind: 'line_item.errors.quantity' } } %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} %td.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' } diff --git a/config/locales/en.yml b/config/locales/en.yml index 1ccd46d5d7..3205b3cab5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -774,3 +774,19 @@ Please follow the instructions there to make your enterprise visible on the Open manage_products: "Manage products" edit_profile_details: "Edit profile details" edit_profile_details_etc: "Change your profile description, images, etc." + bulk_order_management: "Bulk Order Management" + start_date: "Start Date" + end_date: "End Date" + order_cycle: "Order Cycle" + group_buy_unit_size: "Group Buy Unit Size" + total_qtt_ordered: "Total Quantity Ordered" + max_qtt_ordered: "Max Quantity Ordered" + current_fulfilled_units: "Current Fulfilled Units" + max_fulfilled_units: "Max Fulfilled Units" + bulk_management_warning: "WARNING: Some variants do not have a unit value" + action: "   Actions" + ask: "Ask? " + no_orders_found: "No orders found." + order_no: "Order No." + weight_volume: "Weight/Volume" + step: "step" From 5290d0be6b26580ede32d37e3813e03baff74957 Mon Sep 17 00:00:00 2001 From: julienFR88 Date: Wed, 16 Dec 2015 19:37:43 +0100 Subject: [PATCH 008/215] add translation in /cache/code/ofn/openfoodnetwork/app/views/spree/admin --- .../spree/admin/reports/customers.html.haml | 12 ++--- .../reports/order_cycle_management.html.haml | 10 ++-- .../reports/orders_and_distributors.html.haml | 4 +- .../reports/orders_and_fulfillment.html.haml | 10 ++-- .../spree/admin/reports/packing.html.haml | 12 ++--- .../spree/admin/reports/payments.html.haml | 12 ++--- .../reports/products_and_inventory.html.haml | 12 ++--- .../spree/admin/reports/sales_tax.html.haml | 6 +-- .../reports/users_and_enterprises.html.haml | 6 +-- .../admin/reports/xero_invoices.html.haml | 16 +++---- .../shared/_address_form_simple.html.haml | 28 ++++++----- .../admin/shared/_hubs_sidebar.html.haml | 9 ++-- .../shared/_trial_progress_bar.html.haml | 4 +- .../spree/admin/variants/_autocomplete.js.erb | 4 +- config/locales/en.yml | 48 ++++++++++++++++++- 15 files changed, 122 insertions(+), 71 deletions(-) diff --git a/app/views/spree/admin/reports/customers.html.haml b/app/views/spree/admin/reports/customers.html.haml index aef51702a6..4b7004536c 100644 --- a/app/views/spree/admin/reports/customers.html.haml +++ b/app/views/spree/admin/reports/customers.html.haml @@ -2,30 +2,30 @@ %br .row .four.columns.alpha - = label_tag nil, "Distributor: " + = label_tag nil, t(:reports_customers_distributor) = select_tag(:distributor_id, options_from_collection_for_select(@distributors, :id, :name, params[:distributor_id]), {:include_blank => true, :class => "select2 fullwidth"}) .four.columns - = label_tag nil, "Supplier: " + = label_tag nil, t(:reports_customers_supplier) = select_tag(:supplier_id, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id]), {:include_blank => true, :class => "select2 fullwidth"}) .six.columns - = label_tag nil, "Order Cycle: " + = label_tag nil, t(:reports_customers_cycle) = select_tag(:order_cycle_id, options_for_select(report_order_cycle_options(@order_cycles), params[:order_cycle_id]), {:include_blank => true, :class => "select2 fullwidth"}) - = label_tag nil, "Report Type: " + = label_tag nil, t(:reports_customers_type) = select_tag(:report_type, options_for_select(@report_types, @report_type)) %br %br = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) %br = button t(:search) @@ -33,7 +33,7 @@ %br %table#listing_customers.index %thead - %tr{'data-hook' => "orders_header"} + %tr{'data-hook' => "orders_header" } - @report.header.each do |heading| %th=heading %tbody 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 5ab379a669..09ac6f76bb 100644 --- a/app/views/spree/admin/reports/order_cycle_management.html.haml +++ b/app/views/spree/admin/reports/order_cycle_management.html.haml @@ -2,16 +2,16 @@ = render 'date_range_form', f: f .row - .alpha.two.columns= label_tag nil, "Hubs: " + .alpha.two.columns= label_tag nil, t(:report_hubs) .omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Order Cycles: " + .alpha.two.columns= label_tag nil, t(:reports_customers_cycle) .omega.fourteen.columns = f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Payment Methods: " + .alpha.two.columns= label_tag nil, t(:report_payment) .omega.fourteen.columns= select_tag(:payment_method_in, options_for_select(report_payment_method_options(@report.orders), params[:payment_method_in]), {class: "select2 fullwidth", multiple: true}) .row @@ -24,7 +24,7 @@ .row = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) .row = button t(:search) @@ -33,7 +33,7 @@ %br %table#listing_ocm_orders.index %thead - %tr{'data-hook' => "orders_header"} + %tr{'data-hook' => "orders_header" } - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/orders_and_distributors.html.haml b/app/views/spree/admin/reports/orders_and_distributors.html.haml index 23baad395e..9d733cc995 100644 --- a/app/views/spree/admin/reports/orders_and_distributors.html.haml +++ b/app/views/spree/admin/reports/orders_and_distributors.html.haml @@ -2,7 +2,7 @@ = render 'date_range_form', f: f = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) %br = button t(:search) @@ -10,7 +10,7 @@ %br %table#listing_orders.index %thead - %tr{'data-hook' => "orders_header"} + %tr{'data-hook' => t(:report_customers_header)} - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/orders_and_fulfillment.html.haml b/app/views/spree/admin/reports/orders_and_fulfillment.html.haml index 2465fe0ff2..15ad6458cd 100644 --- a/app/views/spree/admin/reports/orders_and_fulfillment.html.haml +++ b/app/views/spree/admin/reports/orders_and_fulfillment.html.haml @@ -2,25 +2,25 @@ = render 'date_range_form', f: f .row - .alpha.two.columns= label_tag nil, "Hubs: " + .alpha.two.columns= label_tag nil, t(:report_hubs) .omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Producers: " + .alpha.two.columns= label_tag nil, t(:report_producers) .omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Order Cycles: " + .alpha.two.columns= label_tag nil, t(:reports_customers_cycle) .omega.fourteen.columns = f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Report Type: " + .alpha.two.columns= label_tag nil, t(:report_type) .omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, @report_type)) .row = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) .row = button t(:search) diff --git a/app/views/spree/admin/reports/packing.html.haml b/app/views/spree/admin/reports/packing.html.haml index 2aa8d8c8b9..d419f9a7cb 100644 --- a/app/views/spree/admin/reports/packing.html.haml +++ b/app/views/spree/admin/reports/packing.html.haml @@ -2,25 +2,25 @@ = render 'date_range_form', f: f .row - .alpha.two.columns= label_tag nil, "Hubs: " + .alpha.two.columns= label_tag nil, t(:report_hubs) .omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Producers: " + .alpha.two.columns= label_tag nil, t(:report_producers) .omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Order Cycles: " + .alpha.two.columns= label_tag nil, t(:report_customers_cycle) .omega.fourteen.columns = f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Report Type: " + .alpha.two.columns= label_tag nil, t(:report_type) .omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, @report_type)) .row = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) .row = button t(:search) @@ -29,7 +29,7 @@ %br %table#listing_orders.index %thead - %tr{'data-hook' => "orders_header"} + %tr{'data-hook' => "orders_header" } - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/payments.html.haml b/app/views/spree/admin/reports/payments.html.haml index 909846f47f..3269267a06 100644 --- a/app/views/spree/admin/reports/payments.html.haml +++ b/app/views/spree/admin/reports/payments.html.haml @@ -3,15 +3,15 @@ .row .four.columns.alpha - = label_tag nil, "Distributor: " - = f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"}) - = label_tag nil, "Report Type: " + = label_tag nil, t(:report_distributor) + = f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => t(:report_all)}, {:class => "select2 fullwidth"}) + = label_tag nil, t(:report_customers_type) %br - = select_tag(:report_type, options_for_select([['Payments By Type',:payments_by_payment_type],['Itemised Payment Totals',:itemised_payment_totals],['Payment Totals',:payment_totals]], @report_type)) + = select_tag(:report_type, options_for_select([[t(:report_payment_by),:payments_by_payment_type],[t(:report_itemised_payment,:itemised_payment_totals],[t(:report_payment_totals,:payment_totals]], @report_type)) %br %br = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) %br %br = button t(:search) @@ -19,7 +19,7 @@ %br %table#listing_orders.index %thead - %tr{'data-hook' => "orders_header"} + %tr{'data-hook' => "orders_header" } - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/products_and_inventory.html.haml b/app/views/spree/admin/reports/products_and_inventory.html.haml index c3470e6047..53ad6649ee 100644 --- a/app/views/spree/admin/reports/products_and_inventory.html.haml +++ b/app/views/spree/admin/reports/products_and_inventory.html.haml @@ -2,40 +2,40 @@ %br .row .four.columns.alpha - = label_tag nil, "Distributor: " + = label_tag nil, t(:report_distributor) = select_tag(:distributor_id, options_from_collection_for_select(@distributors, :id, :name, params[:distributor_id]), {:include_blank => true, :class => "select2 fullwidth"}) .four.columns - = label_tag nil, "Supplier: " + = label_tag nil, t(:reports_customers_supplier) = select_tag(:supplier_id, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id]), {:include_blank => true, :class => "select2 fullwidth"}) .six.columns - = label_tag nil, "Order Cycle: " + = label_tag nil, t(:report_order_cycle) = select_tag(:order_cycle_id, options_for_select(report_order_cycle_options(@order_cycles), params[:order_cycle_id]), {:include_blank => true, :class => "select2 fullwidth"}) - = label_tag nil, "Report Type: " + = label_tag nil, t(:report_type) %br = select_tag(:report_type, options_for_select(@report_types, params[:report_type])) %br %br = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) %br = button t(:search) %br %br %table#listing_products.index %thead - %tr{'data-hook' => "products_header"} + %tr{'data-hook' => "products_header" } - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/sales_tax.html.haml b/app/views/spree/admin/reports/sales_tax.html.haml index b0a115a74b..c91617cc6a 100644 --- a/app/views/spree/admin/reports/sales_tax.html.haml +++ b/app/views/spree/admin/reports/sales_tax.html.haml @@ -3,10 +3,10 @@ .row .four.columns.alpha - = label_tag nil, "Distributor:" + = label_tag nil, t(:report_distributor) = f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"}) = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) %br = button t(:search) @@ -14,7 +14,7 @@ %br %table#listing_orders.index %thead - %tr{'data-hook' => "orders_header"} + %tr{'data-hook' => "orders_header" } - @report.header.each do |heading| %th= heading %tbody diff --git a/app/views/spree/admin/reports/users_and_enterprises.html.haml b/app/views/spree/admin/reports/users_and_enterprises.html.haml index 6820ee04b1..1b57373c38 100644 --- a/app/views/spree/admin/reports/users_and_enterprises.html.haml +++ b/app/views/spree/admin/reports/users_and_enterprises.html.haml @@ -1,10 +1,10 @@ = form_tag spree.users_and_enterprises_admin_reports_url do |f| .row - .alpha.two.columns= label_tag nil, "Enterprises: " + .alpha.two.columns= label_tag nil, t(:report_entreprises) .omega.fourteen.columns= select_tag(:enterprise_id_in, options_from_collection_for_select(Enterprise.all, :id, :name, params[:enterprise_id_in].andand.split(",")), {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, "Users: " + .alpha.two.columns= label_tag nil, t(:report_users) .omega.fourteen.columns= select_tag(:user_id_in, options_from_collection_for_select(Spree::User.all, :id, :email, params[:user_id_in].andand.split(",")), {class: "select2 fullwidth", multiple: true}) -# Might need this later if we add different kinds of reports @@ -14,7 +14,7 @@ .row = check_box_tag :csv - = label_tag :csv, "Download as csv" + = label_tag :csv, t(:report_customers_csv) .row = button t(:search) %br diff --git a/app/views/spree/admin/reports/xero_invoices.html.haml b/app/views/spree/admin/reports/xero_invoices.html.haml index eca51fed4f..8669089f81 100644 --- a/app/views/spree/admin/reports/xero_invoices.html.haml +++ b/app/views/spree/admin/reports/xero_invoices.html.haml @@ -2,31 +2,31 @@ = render 'date_range_form', f: f .row - .four.columns.alpha= label_tag :report_type, "Report Type: " + .four.columns.alpha= label_tag :report_type, t(:report_type) .four.columns.omega= select_tag :report_type, options_for_select(xero_report_types, params[:report_type]), {include_blank: false, class: "select2 fullwidth"} .row - .four.columns.alpha= label_tag nil, "Hub: " + .four.columns.alpha= label_tag nil, t(:report_hubs) .four.columns.omega= f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"}) .row - .four.columns.alpha= label_tag nil, "Order Cycle: " + .four.columns.alpha= label_tag nil, t(:report_order_cycle) .four.columns.omega= f.select(:order_cycle_id_eq, options_for_select(report_order_cycle_options(@order_cycles), params[:q][:order_cycle_id_eq]), {:include_blank => true}, {:class => "select2 fullwidth"}) .row - .four.columns.alpha= label_tag :initial_invoice_number, "Initial invoice number:" + .four.columns.alpha= label_tag :initial_invoice_number, t(:initial_invoice_number) .twelve.columns.omega= text_field_tag :initial_invoice_number, params[:initial_invoice_number] .row - .four.columns.alpha= label_tag :invoice_date, "Invoice date:" + .four.columns.alpha= label_tag :invoice_date, t(:invoice_date) .twelve.columns.omega= text_field_tag :invoice_date, params[:invoice_date], class: 'datetimepicker' .row - .four.columns.alpha= label_tag :due_date, "Due date:" + .four.columns.alpha= label_tag :due_date, t(:due_date) .twelve.columns.omega= text_field_tag :due_date, params[:due_date], class: 'datetimepicker' .row - .four.columns.alpha= label_tag :account_code, "Account code:" + .four.columns.alpha= label_tag :account_code, t(:account_code) .twelve.columns.omega= text_field_tag :account_code, params[:account_code] .row - .four.columns.alpha= label_tag :csv, "Download as CSV:" + .four.columns.alpha= label_tag :csv, t(:report_customers_csv) .twelve.columns.omega= check_box_tag :csv .row .four.columns.alpha= button t(:search) diff --git a/app/views/spree/admin/shared/_address_form_simple.html.haml b/app/views/spree/admin/shared/_address_form_simple.html.haml index 3b8f756d88..36f49bf384 100644 --- a/app/views/spree/admin/shared/_address_form_simple.html.haml +++ b/app/views/spree/admin/shared/_address_form_simple.html.haml @@ -1,18 +1,24 @@ %tr{"data-hook" => "address1"} - %td Address: + %td + = t(:admin_shared_address_1): %td= f.text_field :address1 -%tr{"data-hook" => "address2"} - %td Address (cont.): +%tr{"data-hook" => "address2" } + %td + = t(:admin_shared_address_2): %td= f.text_field :address2 -%tr{"data-hook" => "city"} - %td City: +%tr{"data-hook" => "city" } + %td + = t(:admin_share_city): %td= f.text_field :city -%tr{"data-hook" => "zipcode"} - %td Postcode: +%tr{"data-hook" => "zipcode" } + %td + = t(:admin_share_zipcode): %td= f.text_field :zipcode -%tr{"data-hook" => "country"} - %td Country: +%tr{"data-hook" => "country" } + %td + = t(:admin_share_country): %td= f.collection_select(:country_id, available_countries, :id, :name) -%tr{"data-hook" => "state"} - %td State: +%tr{"data-hook" => "state" } + %td + = t(:admin_share_state): %td= f.collection_select(:state_id, f.object.country.states, :id, :name) diff --git a/app/views/spree/admin/shared/_hubs_sidebar.html.haml b/app/views/spree/admin/shared/_hubs_sidebar.html.haml index 23c536402b..25cdd89685 100644 --- a/app/views/spree/admin/shared/_hubs_sidebar.html.haml +++ b/app/views/spree/admin/shared/_hubs_sidebar.html.haml @@ -1,8 +1,8 @@ - hubs_color = @hubs.count > 0 ? "blue" : "red" -- hubs_color = 'red' if (controller.action_name == 'create' || controller.action_name == 'update') && @object.errors.full_messages.include?("At least one hub must be selected") +- hubs_color = 'red' if (controller.action_name == 'create' || controller.action_name == 'update') && @object.errors.full_messages.include?(t(:hub_sidebar_at_least)) .sidebar_item.omega.four.columns#hubs .four.columns.alpha.header{ class: "#{hubs_color}" } - %span.four.columns.alpha.centered Hubs + %span.four.columns.alpha.centered t(:hub_sidebar_hubs) .four.columns.alpha.list{ class: "#{hubs_color}" } - if @hubs.count > 0 = hidden_field klass, :distributor_ids, :multiple => true, value: nil @@ -17,9 +17,10 @@ %span.icon-arrow-right - else .four.columns.alpha.list-item - %span.three.columns.alpha None Available + %span.three.columns.alpha + t(:hub_sidebar_none_available) %span.one.column.omega %span.icon-remove-sign %a.four.columns.alpha.button{ href: "#{main_app.admin_enterprises_path}", class: "#{hubs_color}" } - MANAGE + t(:hub_sidebar_manage) %span.icon-arrow-right diff --git a/app/views/spree/admin/shared/_trial_progress_bar.html.haml b/app/views/spree/admin/shared/_trial_progress_bar.html.haml index abffdbd6ad..999dd9a13f 100644 --- a/app/views/spree/admin/shared/_trial_progress_bar.html.haml +++ b/app/views/spree/admin/shared/_trial_progress_bar.html.haml @@ -1,7 +1,7 @@ - if enterprise -if shop_trial_in_progress?(enterprise) #trial_progress_bar - = "Your shopfront trial expires in #{remaining_trial_days(enterprise)}." + = t(:shop_trial_in_progress) -elsif shop_trial_expired?(enterprise) #trial_progress_bar - = "Good news! We have decided to extend shopfront trials until further notice (probably around March 2015)." \ No newline at end of file + = t(:shop_trial_expired) \ No newline at end of file diff --git a/app/views/spree/admin/variants/_autocomplete.js.erb b/app/views/spree/admin/variants/_autocomplete.js.erb index 7b52c0a716..11940b4104 100644 --- a/app/views/spree/admin/variants/_autocomplete.js.erb +++ b/app/views/spree/admin/variants/_autocomplete.js.erb @@ -16,8 +16,8 @@
  • Producer: {{variant.producer_name}}
    • -
    • {{t 'sku'}}: {{variant.sku}}
    • -
    • {{t 'on_hand' }}: {{variant.count_on_hand}}
    • +
    • {{t(:admin_variants_sku:)}}: {{variant.sku}}
    • +
    • {{t(admin_variants_on_hand:) }}: {{variant.count_on_hand}}
    {{#if variant.option_values}} diff --git a/config/locales/en.yml b/config/locales/en.yml index 3205b3cab5..cd4af0f08b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -646,7 +646,7 @@ Please follow the instructions there to make your enterprise visible on the Open shop_variant_quantity_max: "max" contact: "Contact" follow: "Follow" - shop_for_products_html: "Shop for %{enterprise} products at:" + shop_for_products_html: "Shop for %{enterprise} products at:" #FIXME change_shop: "Change shop to:" shop_at: "Shop now at:" price_breakdown: "Full price breakdown" @@ -727,7 +727,7 @@ Please follow the instructions there to make your enterprise visible on the Open hub: "Hub" accounts_administration_distributor: "accounts administration distributor" override_product_details: "Override Product Details" - admin_accounts_and_billing: "admin.accounts_and_billing_settings" + admin_accounts_and_billing: "admin.accounts_and_billing_settings" #FIXME producer: "Producer" product: "Product" price: "Price" @@ -790,3 +790,47 @@ Please follow the instructions there to make your enterprise visible on the Open order_no: "Order No." weight_volume: "Weight/Volume" step: "step" + remove_tax: "Remove tax" + admin_variants_on_hand: "on hand" + admin_variants_sku: "sku" + tax_settings: "tax settings" + products_require_tax_category: "products require tax category" + update: "update" + admin_shared_address_1: "Address" + admin_shared_address_2: "Address (cont.)" + admin_share_city: "City" + admin_share_zipcode: "Postcode" + admin_share_country: "Country" + admin_share_state: "State" + hub_sidebar_hubs: "Hubs" + hub_sidebar_none_available: "None Available" + hub_sidebar_manage: "Manage" + hub_sidebar_at_least: "At least one hub must be selected" + hub_sidebar_blue: "blue" + hub_sidebar_red: "red" + shop_trial_in_progress: "Your shopfront trial expires in #{remaining_trial_days(enterprise)}." #FIXME + shop_trial_expired: "Good news! We have decided to extend shopfront trials until further notice (probably around March 2015)." #FIXME + reports_customers_distributor: "Distributor" + reports_customers_supplier: "Supplier" + reports_customers_cycle: "Order Cycle" + report_customers_type: "Report Type" + report_customers_csv: "Download as csv" + report_customers_header: "orders header" + report_producers: "Producers: " + report_type: "Report Type: " + report_hubs: "Hubs: " + report_payment: "Payment Methods: " + report_distributor: "Distributor: " + report_payment_by: 'Payments By Type' + report_itemised_payment: 'Itemised Payment Totals' + report_payment_totals: 'Payment Totals' + report_all: 'all' + report_order_cycle: "Order Cycle: " + report_product_header: "products_header" + report_order_header: "orders_header" + report_entreprises: "Enterprises: " + report_users: "Users: " + initial_invoice_number: "Initial invoice number:" + invoice_date: "Invoice date:" + due_date: "Due date:" + account_code: "Account code:" From 8a390dd2374257b227e4582b358490b4ed827b72 Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Sat, 19 Dec 2015 11:05:04 +0100 Subject: [PATCH 009/215] fixing test failing after i18n changes --- .../accounts_and_billing_settings/edit.html.haml | 2 +- app/views/admin/order_cycles/edit.html.haml | 4 ++-- app/views/admin/order_cycles/new.html.haml | 4 ++-- .../admin/order_cycles/set_coordinator.html.haml | 4 ++-- .../admin/variant_overrides/_products.html.haml | 12 ++++++++---- .../spree/admin/orders/bulk_management.html.haml | 2 +- .../spree/admin/overview/_unconfirmed.html.haml | 4 ++-- .../overview/single_enterprise_dashboard.html.haml | 2 +- app/views/spree/admin/reports/payments.html.haml | 2 +- app/views/spree/admin/variants/_autocomplete.js.erb | 4 ++-- config/locales/en.yml | 12 +++++------- 11 files changed, 27 insertions(+), 25 deletions(-) diff --git a/app/views/admin/accounts_and_billing_settings/edit.html.haml b/app/views/admin/accounts_and_billing_settings/edit.html.haml index 434ad6884c..7a74a59120 100644 --- a/app/views/admin/accounts_and_billing_settings/edit.html.haml +++ b/app/views/admin/accounts_and_billing_settings/edit.html.haml @@ -73,7 +73,7 @@ .six.columns.omega.step.text-center .form-buttons{"data-hook" => "buttons"} - =link_to_with_icon "icon-ok-sign", t(:finalise_user_invoice ), + =link_to_with_icon "icon-ok-sign", t(:finalise_user_invoices ), main_app.start_job_admin_accounts_and_billing_settings_path(job: { name: "finalize_account_invoices" }), class: "button fullwidth" diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index ed9b713d2c..81496bcdb8 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -4,8 +4,8 @@ = button_to "Notify producers", main_app.notify_producers_admin_order_cycle_path, :id => 'admin_notify_producers', :confirm => 'Are you sure?' -%h1 - =t'edit_order_cycle' +%h1 + =t'edit_order_cycle' - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' diff --git a/app/views/admin/order_cycles/new.html.haml b/app/views/admin/order_cycles/new.html.haml index 4c0bee9e5a..a257bf3c0b 100644 --- a/app/views/admin/order_cycles/new.html.haml +++ b/app/views/admin/order_cycles/new.html.haml @@ -1,5 +1,5 @@ -%h1 - =t'new_order_cycle' +%h1 + =t'new_order_cycle' - ng_controller = order_cycles_simple_form ? 'AdminSimpleCreateOrderCycleCtrl' : 'AdminCreateOrderCycleCtrl' = admin_inject_order_cycle_instance diff --git a/app/views/admin/order_cycles/set_coordinator.html.haml b/app/views/admin/order_cycles/set_coordinator.html.haml index 1a31235f24..0216edee54 100644 --- a/app/views/admin/order_cycles/set_coordinator.html.haml +++ b/app/views/admin/order_cycles/set_coordinator.html.haml @@ -1,5 +1,5 @@ %h4.text-center - =t'select_a_coordinator_for_your_order_cycle' + =t'select_a_coordinator_for_your_order_cycle' %br @@ -10,7 +10,7 @@ .ten.columns = select_tag :coordinator_id, options_for_select(permitted_coordinating_enterprise_options_for(@order_cycle)), { 'required' => true, class: 'select2 fullwidth'} .two.columns.alpha - = f.submit "Continue >" + = f.submit "#{t(:continue)} >" .two.columns.omega   diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index 4ee3644798..a2241dca39 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,10 +1,14 @@ %table.index.bulk{ng: {show: 'hub'}} %thead %tr - %th t(:producer) - %th t(:product) - %th t(:price) - %th t(:on_hand) + %th + = t(:producer) + %th + = t(:product) + %th + = t(:price) + %th + = t(:on_hand) %tbody{ng: {repeat: 'product in products | hubPermissions:hubPermissions:hub.id'}} = render 'admin/variant_overrides/products_product' = render 'admin/variant_overrides/products_variants' diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index cc7c0f171a..d81bff9b86 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -164,7 +164,7 @@ %td.variant{ 'ng-show' => 'columns.variant.visible' } %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.show-dirty{ :type => 'number', :name => 'quantity', :id => 'quantity', ng: { model: "line_item.quantity", change: "updateOnQuantity(line_item)", required: "true", class: '{"update-error": line_item.errors.quantity}' }, min: 1, t(:step): 1 } + %input.show-dirty{ :type => 'number', :name => 'quantity', :id => 'quantity', ng: { model: "line_item.quantity", change: "updateOnQuantity(line_item)", required: "true", class: '{"update-error": line_item.errors.quantity}' }, min: 1, step: 1 } %span.error{ ng: { bind: 'line_item.errors.quantity' } } %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} %td.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' } diff --git a/app/views/spree/admin/overview/_unconfirmed.html.haml b/app/views/spree/admin/overview/_unconfirmed.html.haml index 05c4df2a0d..5ff0e17a84 100644 --- a/app/views/spree/admin/overview/_unconfirmed.html.haml +++ b/app/views/spree/admin/overview/_unconfirmed.html.haml @@ -1,4 +1,4 @@ - @enterprises.unconfirmed.each do |enterprise| .alert - %h6= "Action Required: Please confirm the email address for #{enterprise.name}." - %span.message= "We've sent a confirmation email to #{enterprise.email}, so please check there for further instructions. Thanks!" \ No newline at end of file + %h6= "#{t :spree_admin_overview_action_required}: #{t :spree_admin_single_enterprise_alert_mail_confirmation} #{enterprise.name}." + %span.message= "#{t :spree_admin_single_enterprise_alert_mail_sent} #{enterprise.email}. #{t :spree_admin_overview_check_your_inbox}" diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml index 119c907678..14bc30e23b 100644 --- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml @@ -31,7 +31,7 @@ .alert-box = t "spree_admin_single_enterprise_alert_mail_confirmation" %strong= "#{@enterprise.name}." - t(:spree_admin_single_enterprise_alert_mail_sent) + = t(:spree_admin_single_enterprise_alert_mail_sent) %strong= "#{@enterprise.email}." = link_to(t('resend'), main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: @enterprise.email } ), method: :post) %a.close{ href: "#" } × diff --git a/app/views/spree/admin/reports/payments.html.haml b/app/views/spree/admin/reports/payments.html.haml index 3269267a06..a0e8f1d1d8 100644 --- a/app/views/spree/admin/reports/payments.html.haml +++ b/app/views/spree/admin/reports/payments.html.haml @@ -7,7 +7,7 @@ = f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => t(:report_all)}, {:class => "select2 fullwidth"}) = label_tag nil, t(:report_customers_type) %br - = select_tag(:report_type, options_for_select([[t(:report_payment_by),:payments_by_payment_type],[t(:report_itemised_payment,:itemised_payment_totals],[t(:report_payment_totals,:payment_totals]], @report_type)) + = select_tag(:report_type, options_for_select([[t(:report_payment_by),:payments_by_payment_type],[t(:report_itemised_payment),:itemised_payment_totals],[t(:report_payment_totals),:payment_totals]], @report_type)) %br %br = check_box_tag :csv diff --git a/app/views/spree/admin/variants/_autocomplete.js.erb b/app/views/spree/admin/variants/_autocomplete.js.erb index 11940b4104..41b0e84550 100644 --- a/app/views/spree/admin/variants/_autocomplete.js.erb +++ b/app/views/spree/admin/variants/_autocomplete.js.erb @@ -16,8 +16,8 @@
  • Producer: {{variant.producer_name}}
    • -
    • {{t(:admin_variants_sku:)}}: {{variant.sku}}
    • -
    • {{t(admin_variants_on_hand:) }}: {{variant.count_on_hand}}
    • +
    • {{t 'sku'}}: {{variant.sku}}
    • +
    • {{t 'on_hand'}}: {{variant.count_on_hand}}
    {{#if variant.option_values}} diff --git a/config/locales/en.yml b/config/locales/en.yml index cd4af0f08b..9eafa25770 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -708,7 +708,7 @@ Please follow the instructions there to make your enterprise visible on the Open admin_settings: "Settings" update_invoice: "Update Invoices" finalise_invoice: "Finalise Invoices" - finalise_user_invoice: "Finalise User Invoice" + finalise_user_invoices: "Finalise User Invoices" finalise_user_invoice_explained: "Use this button to finalize all invoices in the system for the previous calendar month. This task can be set up to run automatically once a month." manually_run_task: "Manually Run Task " update_user_invoices: "Update User Invoices" @@ -732,7 +732,7 @@ Please follow the instructions there to make your enterprise visible on the Open product: "Product" price: "Price" on_hand: "On hand" - save_changes: "Save changes" + save_changes: "Save Changes" update_action: "update()" #FIXME admin_entreprise_groups_what_s_this: "What's this ?" spree_admin_overview_enterprises_header: "My Enterprises" @@ -760,6 +760,8 @@ Please follow the instructions there to make your enterprise visible on the Open dashbord: "Dashboard" spree_admin_single_enterprise_alert_mail_confirmation: "Please confirm the email address for" spree_admin_single_enterprise_alert_mail_sent: "We've sent an email to" + spree_admin_overview_action_required: "Action Required" + spree_admin_overview_check_your_inbox: "Please check you inbox for furher instructions. Thanks!" change_package: "Change Package" spree_admin_single_enterprise_hint: "Hint: To allow people to find you, turn on your visibility under" your_profil_live: "Your profile live" @@ -789,13 +791,9 @@ Please follow the instructions there to make your enterprise visible on the Open no_orders_found: "No orders found." order_no: "Order No." weight_volume: "Weight/Volume" - step: "step" remove_tax: "Remove tax" - admin_variants_on_hand: "on hand" - admin_variants_sku: "sku" - tax_settings: "tax settings" + tax_settings: "Tax Settings" products_require_tax_category: "products require tax category" - update: "update" admin_shared_address_1: "Address" admin_shared_address_2: "Address (cont.)" admin_share_city: "City" From ec6962ea2c86c31f7baea8756c4924371af193a7 Mon Sep 17 00:00:00 2001 From: Florian Vallen Date: Sun, 13 Dec 2015 18:18:09 +0100 Subject: [PATCH 010/215] Implements i18n translation for admin interface it takes the i18n.js files from darkswarm. This should be changed later and added to the shared js folder. At best, the namespacing for yml files should work just as they work together with ruby, so general: sure: sure? should point to t(general.sure) --- app/assets/javascripts/admin/all.js | 3 +++ app/assets/javascripts/admin/filters/translate.js.coffee | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 app/assets/javascripts/admin/filters/translate.js.coffee diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index c0fa530626..c33ac741bd 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -42,5 +42,8 @@ //= require textAngular.min.js //= require textAngular-sanitize.min.js //= require ../shared/bindonce.min.js +//= require darkswarm/i18n.js +//= require darkswarm/i18n.translate.js + //= require_tree . diff --git a/app/assets/javascripts/admin/filters/translate.js.coffee b/app/assets/javascripts/admin/filters/translate.js.coffee new file mode 100644 index 0000000000..0fc0aa052d --- /dev/null +++ b/app/assets/javascripts/admin/filters/translate.js.coffee @@ -0,0 +1,8 @@ +angular.module('ofn.admin').filter "translate", -> + (key, options) -> + t(key, options) + +angular.module('ofn.admin').filter "t", -> + (key, options) -> + t(key, options) + From 7192f12a49304cd5f47aa0dff03d7453f8671972 Mon Sep 17 00:00:00 2001 From: Florian Vallen Date: Sun, 13 Dec 2015 18:18:33 +0100 Subject: [PATCH 011/215] first js translation for admin interface --- .../admin/controllers/enterprise_roles_controller.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/controllers/enterprise_roles_controller.js.coffee b/app/assets/javascripts/admin/controllers/enterprise_roles_controller.js.coffee index 026913a263..71d0097c6b 100644 --- a/app/assets/javascripts/admin/controllers/enterprise_roles_controller.js.coffee +++ b/app/assets/javascripts/admin/controllers/enterprise_roles_controller.js.coffee @@ -7,5 +7,5 @@ angular.module("ofn.admin").controller "AdminEnterpriseRolesCtrl", ($scope, Ente $scope.EnterpriseRoles.create($scope.user_id, $scope.enterprise_id) $scope.delete = (enterprise_role) -> - if confirm("Are you sure?") + if confirm(t('are_you_sure')) $scope.EnterpriseRoles.delete enterprise_role From 69ab1132015cac2640add8ca29f88f7c7480b500 Mon Sep 17 00:00:00 2001 From: ludivinecp Date: Mon, 14 Dec 2015 19:49:45 +0100 Subject: [PATCH 012/215] translations in app/assets/javascripts/admin/ --- .../admin/bulk_product_update.js.coffee | 56 +++++++++---------- .../javascripts/admin/enterprise_fees.js | 2 +- config/locales/en.yml | 29 +++++++++- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index b3d24f00c0..345e9105a7 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -4,32 +4,32 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.StatusMessage = StatusMessage $scope.columns = Columns.setColumns - producer: {name: "Producer", visible: true} - sku: {name: "SKU", visible: false} - name: {name: "Name", visible: true} - 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} - tax_category: {name: "Tax Category", visible: false} - inherits_properties: {name: "Inherits Properties?", visible: false} - available_on: {name: "Available On", visible: false} + producer: {name: t("label_producers"), visible: true} + sku: {name: t("products_sku"), visible: false} + name: {name: t("products_name"), visible: true} + unit: {name: t("products_unit"), visible: true} + price: {name: t("products_price"), visible: true} + on_hand: {name: t("products_on_hand"), visible: true} + on_demand: {name: t("products_on_demand"), visible: false} + category: {name: t("products_category"), visible: false} + tax_category: {name: t("products_tax_category"), visible: false} + inherits_properties: {name: t("products_inherits_properties"), visible: false} + available_on: {name: t("products_available_on"), visible: false} $scope.variant_unit_options = VariantUnitManager.variantUnitOptions() $scope.filterableColumns = [ - { name: "Producer", db_column: "producer_name" }, - { name: "Name", db_column: "name" } + { name: t("label_producers"), db_column: "producer_name" }, + { name: t("name"), db_column: "name" } ] $scope.filterTypes = [ - { name: "Equals", predicate: "eq" }, - { name: "Contains", predicate: "cont" } + { name: t("equals"), predicate: "eq" }, + { name: t("contains"), predicate: "cont" } ] $scope.optionTabs = - filters: { title: "Filter Products", visible: false } + filters: { title: t("filter_products"), visible: false } $scope.producers = producers @@ -105,7 +105,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.categoryFilter = "0" $scope.editWarn = (product, variant) -> - if (DirtyProducts.count() > 0 and confirm("Unsaved changes will be lost. Continue anyway?")) or (DirtyProducts.count() == 0) + if (DirtyProducts.count() > 0 and confirm(t("unsaved_changes_confirmation"))) or (DirtyProducts.count() == 0) window.location = "/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit" @@ -150,14 +150,14 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout if !$scope.variantSaved(variant) $scope.removeVariant(product, variant) else - if confirm("Are you sure?") + if confirm(t("are_you_sure")) $http( method: "DELETE" url: "/api/products/" + product.permalink_live + "/variants/" + variant.id + "/soft_delete" ).success (data) -> $scope.removeVariant(product, variant) else - alert("The last variant cannot be deleted!") + alert(t("delete_product_variant")) $scope.removeVariant = (product, variant) -> product.variants.splice product.variants.indexOf(variant), 1 @@ -194,7 +194,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout if productsToSubmit.length > 0 $scope.updateProducts productsToSubmit # Don't submit an empty list else - StatusMessage.display 'alert', 'No changes to save.' + StatusMessage.display 'alert', t("products_change") $scope.updateProducts = (productsToSubmit) -> @@ -212,10 +212,10 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout ).error (data, status) -> if status == 400 && data.errors? && data.errors.length > 0 errors = error + "\n" for error in data.errors - alert "Saving failed with the following error(s):\n" + errors - $scope.displayFailure "Save failed due to invalid data" + alert t("products_update_error") + errors + $scope.displayFailure t("products_update_error") else - $scope.displayFailure "Server returned with error status: " + status + $scope.displayFailure t("products_update_error_data") + status $scope.packProduct = (product) -> @@ -253,21 +253,21 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.displayUpdating = -> - StatusMessage.display 'progress', 'Saving...' + StatusMessage.display 'progress', t("saving") $scope.displaySuccess = -> - StatusMessage.display 'success', 'Changes saved.' + StatusMessage.display 'success',t("products_changes_saved") $scope.displayFailure = (failMessage) -> - StatusMessage.display 'failure', "Saving failed. #{failMessage}" + StatusMessage.display 'failure', t("products_update_error_msg") + "#{failMessage}" $scope.displayDirtyProducts = -> if DirtyProducts.count() > 0 - message = if DirtyProducts.count() == 1 then "one product" else DirtyProducts.count() + " products" - StatusMessage.display 'notice', "Changes to #{message} remain unsaved." + message = if DirtyProducts.count() == 1 then t("one_product") else DirtyProducts.count() + t("products") + StatusMessage.display 'notice', t("changes_to") + "#{message}" + t("remain_unsaved.") else StatusMessage.clear() diff --git a/app/assets/javascripts/admin/enterprise_fees.js b/app/assets/javascripts/admin/enterprise_fees.js index b815cd4266..7aafc25df9 100644 --- a/app/assets/javascripts/admin/enterprise_fees.js +++ b/app/assets/javascripts/admin/enterprise_fees.js @@ -34,7 +34,7 @@ angular.module('enterprise_fees', []) return function(scope, element, attrs) { if(scope.enterprise_fee.id) { var url = "/admin/enterprise_fees/" + scope.enterprise_fee.id - var html = ''; + var html = ''; //var html = 'Delete Delete'; element.append(html); } diff --git a/config/locales/en.yml b/config/locales/en.yml index 9eafa25770..f8a71b874b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -324,6 +324,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_cart_empty: "Cart empty" products_edit_cart: "Edit your cart" products_from: from + products_change: "No changes to save" + products_update_error: "Saving failed with the following error(s):" + products_update_error_msg: "Saving failed." + products_update_error_data: "Save failed due to invalid data:" + products_changes_saved: "Changes saved." search_no_results_html: "Sorry, no results found for %{query}. Try another search?" @@ -337,6 +342,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using groups_search: "Search name or keyword" groups_no_groups: "No groups found" groups_about: "About Us" + groups_producers: "Our producers" groups_hubs: "Our hubs" groups_contact_web: Contact @@ -430,7 +436,16 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_variant: Variant products_quantity: Quantity products_availabel: Available? - products_price: Price + products_price: "Price" + products_sku: "SKU" + products_name: "name" + products_unit: "unit" + products_on_hand: "on hand" + products_on_demand: "on demand" + products_category: "category" + products_tax_category: "tax category" + products_available_on: "available on" + products_inherits_properties: "Inherits Properties ?" register_title: Register @@ -832,3 +847,15 @@ Please follow the instructions there to make your enterprise visible on the Open invoice_date: "Invoice date:" due_date: "Due date:" account_code: "Account code:" + equals: "Equals" + contains: "contains" + filter_products: "Filter Products" + delete_product_variant: "The last variant cannot be deleted!" + progress: "progress" + saving: "Saving.." + success: "success" + failure: "failure" + unsaved_changes_confirmation: "Unsaved changes will be lost. Continue anyway?" + one_product: "one product" + changes_to: "Changes to" + remain_unsaved: "remain unsaced" From e16e3fba40d4e9521054a51d683f21bf01fdb1bb Mon Sep 17 00:00:00 2001 From: victor goutay Date: Mon, 14 Dec 2015 20:30:50 +0100 Subject: [PATCH 013/215] translations in app/assets/javascripts/admin/ --- .../admin/bulk_order_management.js.coffee | 28 +++++++-------- .../line_items_controller.js.coffee | 36 +++++++++---------- config/locales/en.yml | 15 ++++++++ 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index 7d0572635d..c2250954b7 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -12,25 +12,25 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.startDate = formatDate start $scope.endDate = formatDate end $scope.quickSearch = "" - $scope.bulkActions = [ { name: "Delete Selected", callback: $scope.deleteLineItems } ] + $scope.bulkActions = [ { name: t("bom_actions_delete"), callback: $scope.deleteLineItems } ] $scope.selectedBulkAction = $scope.bulkActions[0] $scope.selectedUnitsProduct = {}; $scope.selectedUnitsVariant = {}; $scope.sharedResource = false $scope.columns = Columns.setColumns - order_no: { name: "Order No.", visible: false } - full_name: { name: "Name", visible: true } - email: { name: "Email", visible: false } - phone: { name: "Phone", visible: false } - order_date: { name: "Order Date", visible: true } - producer: { name: "Producer", visible: true } - order_cycle: { name: "Order Cycle", visible: false } - hub: { name: "Hub", visible: false } - variant: { name: "Variant", visible: true } - quantity: { name: "Quantity", visible: true } - max: { name: "Max", visible: true } - final_weight_volume: { name: "Weight/Volume", visible: false } - price: { name: "Price", visible: false } + order_no: { name: t("bom_no"), visible: false } + full_name: { name: t("name"), visible: true } + email: { name: t("email"), visible: false } + phone: { name: t("phone"), visible: false } + order_date: { name: t("bom_date"), visible: true } + producer: { name: t("producer"), visible: true } + order_cycle: { name: t("bom_cycle"), visible: false } + hub: { name: t("bom_hub"), visible: false } + variant: { name: t("bom_variant"), visible: true } + quantity: { name: t("bom_quantity"), visible: true } + max: { name: t("bom_max"), visible: true } + final_weight_volume: { name: t("bom_final_weigth_volume"), visible: false } + price: { name: t("bom_price"), visible: false } $scope.initialise = -> $scope.initialiseVariables() authorise_api_reponse = "" diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 38c7f0ba33..94e17b563c 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -6,27 +6,27 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.confirmDelete = true $scope.startDate = formatDate daysFromToday -7 $scope.endDate = formatDate daysFromToday 1 - $scope.bulkActions = [ { name: "Delete Selected", callback: 'deleteLineItems' } ] + $scope.bulkActions = [ { name: t("bom_actions_delete"), callback: 'deleteLineItems' } ] $scope.selectedUnitsProduct = {}; $scope.selectedUnitsVariant = {}; $scope.sharedResource = false $scope.columns = Columns.setColumns - order_no: { name: "Order No.", visible: false } - full_name: { name: "Name", visible: true } - email: { name: "Email", visible: false } - phone: { name: "Phone", visible: false } - order_date: { name: "Order Date", visible: true } - producer: { name: "Producer", visible: true } - order_cycle: { name: "Order Cycle", visible: false } - hub: { name: "Hub", visible: false } - variant: { name: "Variant", visible: true } - quantity: { name: "Quantity", visible: true } - max: { name: "Max", visible: true } - final_weight_volume: { name: "Weight/Volume", visible: false } - price: { name: "Price", visible: false } + order_no: { name: t("bom_no"), visible: false } + full_name: { name: t("name"), visible: true } + email: { name: t("email"), visible: false } + phone: { name: t("phone"), visible: false } + order_date: { name: t("bom_date"), visible: true } + producer: { name: t("producer"), visible: true } + order_cycle: { name: t("bom_cycle"), visible: false } + hub: { name: t("bom_hub"), visible: false } + variant: { name: t("bom_variant"), visible: true } + quantity: { name: t("bom_quantity"), visible: true } + max: { name: t("bom_max"), visible: true } + final_weight_volume: { name: t("bom_final_weigth_volume"), visible: false } + price: { name: t("bom_price"), visible: false } $scope.confirmRefresh = -> - LineItems.allSaved() || confirm("Unsaved changes exist and will be lost if you continue.") + LineItems.allSaved() || confirm(t("unsaved_changes_warning")) $scope.resetSelectFilters = -> $scope.distributorFilter = blankOption().id @@ -69,12 +69,12 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.bulk_order_form.$setPristine() $scope.saving = false ).catch -> - alert "Some errors must be resolved be before you can update orders.\nAny fields with red borders contain errors." + alert "#{t("unsaved_changes_warning")}" else - alert "Some errors must be resolved be before you can update orders.\nAny fields with red borders contain errors." + alert "#{t("unsaved_changes_warning")}" $scope.deleteLineItem = (lineItem) -> - if ($scope.confirmDelete && confirm("Are you sure?")) || !$scope.confirmDelete + if ($scope.confirmDelete && confirm(t("are_you_sure"))) || !$scope.confirmDelete LineItems.delete lineItem, => $scope.lineItems.splice $scope.lineItems.indexOf(lineItem), 1 diff --git a/config/locales/en.yml b/config/locales/en.yml index f8a71b874b..fd5460fa3a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -221,6 +221,20 @@ en: order_payment_paypal_successful: Your payment via PayPal has been processed successfully. order_hub_info: Hub Info + bom_no: "Order no." + bom_date: "Order date" + bom_cycle: "Order cycle" + bom_quantity: "Quantity" + bom_max: "Max" + bom_hub: "Hub" + bom_variant: "Variant" + bom_final_weigth_volume: "Weight/Volume" + bom_price: "Price" + bom_actions_delete: "Delete Selected" + bom_order_error: "Some errors must be resolved before you can update orders.\nAny fields with red borders contain errors." + + unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." + products: "Products" products_in: "in %{oc}" products_at: "at %{distributor}" @@ -430,6 +444,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using producers_signup_cta_headline: Join now! producers_signup_cta_action: Join now producers_signup_detail: Here's the detail. + producer: Producer products_item: Item products_description: Description From 30141bd488b969a19b4028520c91846860bb1fe2 Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Mon, 14 Dec 2015 20:18:45 +0100 Subject: [PATCH 014/215] few translations in app/assets/javascripts/admin/ --- .../enterprises/controllers/enterprise_controller.js.coffee | 6 ++++-- .../controllers/enterprises_controller.js.coffee | 1 + config/locales/en.yml | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee index d48f2a42f8..05c9b40f99 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee @@ -6,7 +6,7 @@ angular.module("admin.enterprises") $scope.navClear = NavigationCheck.clear $scope.pristineEmail = $scope.Enterprise.email $scope.menu = SideMenu - $scope.newManager = { id: '', email: 'Add a manager...' } + $scope.newManager = { id: '', email: (t("add_manager")) } # Provide a callback for generating warning messages displayed before leaving the page. This is passed in # from a directive "nav-check" in the page - if we pass it here it will be called in the test suite, @@ -31,4 +31,6 @@ angular.module("admin.enterprises") if (user for user in $scope.Enterprise.users when user.id == manager.id).length == 0 $scope.Enterprise.users.push manager else - alert "#{manager.email} is already a manager!" + alert ("#{manager.email}" + " " + t("is_already_manager")) + + diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee index 3d8bfa6446..30710d1538 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee @@ -11,3 +11,4 @@ angular.module("admin.enterprises").controller 'enterprisesCtrl', ($scope, $q, E package: { name: "Package", visible: true } status: { name: "Status", visible: true } manage: { name: "Manage", visible: true } + diff --git a/config/locales/en.yml b/config/locales/en.yml index fd5460fa3a..02de2b816e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -874,3 +874,5 @@ Please follow the instructions there to make your enterprise visible on the Open one_product: "one product" changes_to: "Changes to" remain_unsaved: "remain unsaced" + add_manager: "Add a manager" + is_already_manager: "is already a manager!" From ed48b691e1a17636ca9c66b4a4c557cbc925067a Mon Sep 17 00:00:00 2001 From: knopfler81 Date: Mon, 14 Dec 2015 20:20:01 +0100 Subject: [PATCH 015/215] translations in app/assets/javascripts/admin --- ...erprise_relationships_controller.js.coffee | 2 +- .../variant_overrides_controller.js.coffee | 2 +- .../side_menu_controller.js.coffee | 10 +++---- .../enterprise_controller.js.coffee | 2 +- .../side_menu_controller.js.coffee | 26 +++++++++---------- config/locales/en.yml | 17 ++++++++++++ 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee b/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee index 47083d443a..153acb2443 100644 --- a/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee +++ b/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee @@ -7,7 +7,7 @@ angular.module("ofn.admin").controller "AdminEnterpriseRelationshipsCtrl", ($sco $scope.EnterpriseRelationships.create($scope.parent_id, $scope.child_id, $scope.permissions) $scope.delete = (enterprise_relationship) -> - if confirm("Are you sure?") + if confirm(t("are_you_sure")) $scope.EnterpriseRelationships.delete enterprise_relationship $scope.toggleKeyword = (string, key) -> diff --git a/app/assets/javascripts/admin/controllers/variant_overrides_controller.js.coffee b/app/assets/javascripts/admin/controllers/variant_overrides_controller.js.coffee index bcc633805f..cb3197f86e 100644 --- a/app/assets/javascripts/admin/controllers/variant_overrides_controller.js.coffee +++ b/app/assets/javascripts/admin/controllers/variant_overrides_controller.js.coffee @@ -40,7 +40,7 @@ angular.module("ofn.admin").controller "AdminVariantOverridesCtrl", ($scope, $ti $scope.update = -> if DirtyVariantOverrides.count() == 0 - StatusMessage.display 'alert', 'No changes to save.' + StatusMessage.display 'alert', 'No change to save ' else StatusMessage.display 'progress', 'Saving...' DirtyVariantOverrides.save() diff --git a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee index 7b9a8165a1..45ca04bfe7 100644 --- a/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee @@ -5,11 +5,11 @@ angular.module("admin.enterprise_groups") $scope.menu.setItems [ { name: 'Primary Details', icon_class: "icon-user" } - { name: 'Users', icon_class: "icon-user" } - { name: 'About', icon_class: "icon-pencil" } - { name: 'Images', icon_class: "icon-picture" } - { name: 'Contact', icon_class: "icon-phone" } - { name: 'Web', icon_class: "icon-globe" } + { name: (t('users')), icon_class: "icon-user" } + { name: (t('about')), icon_class: "icon-pencil" } + { name: (t('images')), icon_class: "icon-picture" } + { name: (t('contact')), icon_class: "icon-phone" } + { name: (t('web')), icon_class: "icon-globe" } ] $scope.select(0) diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee index 05c9b40f99..34b4f99ca2 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee @@ -6,7 +6,7 @@ angular.module("admin.enterprises") $scope.navClear = NavigationCheck.clear $scope.pristineEmail = $scope.Enterprise.email $scope.menu = SideMenu - $scope.newManager = { id: '', email: (t("add_manager")) } + $scope.newManager = { id: '', email: (t('add_manager')) } # Provide a callback for generating warning messages displayed before leaving the page. This is passed in # from a directive "nav-check" in the page - if we pass it here it will be called in the test suite, diff --git a/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee index 45a5d068a4..71c03adbd0 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee @@ -5,19 +5,19 @@ angular.module("admin.enterprises") $scope.select = SideMenu.select $scope.menu.setItems [ - { name: 'Primary Details', icon_class: "icon-home" } - { name: 'Users', icon_class: "icon-user" } - { name: 'Address', icon_class: "icon-map-marker" } - { name: 'Contact', icon_class: "icon-phone" } - { name: 'Social', icon_class: "icon-twitter" } - { name: 'About', icon_class: "icon-pencil" } - { name: 'Business Details', icon_class: "icon-briefcase" } - { name: 'Images', icon_class: "icon-picture" } - { name: "Properties", icon_class: "icon-tags", show: "showProperties()" } - { name: "Shipping Methods", icon_class: "icon-truck", show: "showShippingMethods()" } - { name: "Payment Methods", icon_class: "icon-money", show: "showPaymentMethods()" } - { name: "Enterprise Fees", icon_class: "icon-tasks", show: "showEnterpriseFees()" } - { name: "Shop Preferences", icon_class: "icon-shopping-cart", show: "showShopPreferences()" } + { name: (t('primary_details')), icon_class: "icon-home" } + { name: (t('users')), icon_class: "icon-user" } + { name: (t('address')), icon_class: "icon-map-marker" } + { name: (t('contact')), icon_class: "icon-phone" } + { name: (t('social')), icon_class: "icon-twitter" } + { name: (t('about')), icon_class: "icon-pencil" } + { name: (t('business_details')), icon_class: "icon-briefcase" } + { name: (t('images')), icon_class: "icon-picture" } + { name: (t("properties")), icon_class: "icon-tags", show: "showProperties()" } + { name: (t("shipping_methods")), icon_class: "icon-truck", show: "showShippingMethods()" } + { name: (t("payment_methods")), icon_class: "icon-money", show: "showPaymentMethods()" } + { name: (t("enterprise_fees")), icon_class: "icon-tasks", show: "showEnterpriseFees()" } + { name: (t("shop_preferences")), icon_class: "icon-shopping-cart", show: "showShopPreferences()" } ] $scope.select(0) diff --git a/config/locales/en.yml b/config/locales/en.yml index 02de2b816e..b9c32d72f5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -876,3 +876,20 @@ Please follow the instructions there to make your enterprise visible on the Open remain_unsaved: "remain unsaced" add_manager: "Add a manager" is_already_manager: "is already a manager!" + no_change_to_save: " No change to save" + add_manager: "Add a manager" + users: "Users" + about: "About" + images: "Images" + contact: "Contact" + web: "Web" + primary_details: "Primary Details" + adrdress: "Address" + contact: "Contact" + social: "Social" + business_details: "Business Details" + properties: "Properties" + shipping_methods: "Shipping Methods" + payment_methods: "Payment Methods" + enterprise_fees: "Enterprise Fees" + shop_preferences: "Shop Preferences" From 2a4f3f9ae4fca3300a3c679c64500bcae5f8580c Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Mon, 21 Dec 2015 06:52:30 +0100 Subject: [PATCH 016/215] making test pass --- .../admin/bulk_product_update.js.coffee | 2 +- .../products/bulk_edit/_filters.html.haml | 6 ++++-- config/locales/en.yml | 21 ++++++++++--------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 345e9105a7..289e4fdf80 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -4,7 +4,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.StatusMessage = StatusMessage $scope.columns = Columns.setColumns - producer: {name: t("label_producers"), visible: true} + producer: {name: t("products_producer"), visible: true} sku: {name: t("products_sku"), visible: false} name: {name: t("products_name"), visible: true} unit: {name: t("products_unit"), visible: true} diff --git a/app/views/spree/admin/products/bulk_edit/_filters.html.haml b/app/views/spree/admin/products/bulk_edit/_filters.html.haml index 99c1067663..cf9d01a4ae 100644 --- a/app/views/spree/admin/products/bulk_edit/_filters.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_filters.html.haml @@ -4,11 +4,13 @@ %br %input.search{ :class => "four columns alpha", 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' } .filter_select{ :class => "four columns" } - %label{ :for => 'producer_filter' }Producer + %label{ :for => 'producer_filter' } + = t 'producer' %br %select{ :class => "four columns alpha", :id => 'producer_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'producerFilter', 'ng-options' => 'producer.id as producer.name for producer in filterProducers' } .filter_select{ :class => "four columns" } - %label{ :for => 'category_filter' }Category + %label{ :for => 'category_filter' } + = t 'category' %br %select{ :class => "four columns alpha", :id => 'category_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'categoryFilter', 'ng-options' => 'taxon.id as taxon.name for taxon in filterTaxons'} %div{ :class => "one column" }   diff --git a/config/locales/en.yml b/config/locales/en.yml index b9c32d72f5..ef8da66414 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -17,8 +17,8 @@ en: title: Open Food Network welcome_to: 'Welcome to ' search_by_name: Search by name or suburb... - producers: Aussie Producers - producers_join: Australian producers are now welcome to join the Open Food Network. + producers: Aussie Producers #FIXME + producers_join: Australian producers are now welcome to join the Open Food Network. #FIXME charges_sales_tax: Charges GST? print_invoice: "Print Invoice" send_invoice: "Send Invoice" @@ -48,12 +48,12 @@ en: invoice_column_tax: "GST" invoice_column_price: "Price" - logo: "Logo (640x130)" - logo_mobile: "Mobile logo (75x26)" - logo_mobile_svg: "Mobile logo (SVG)" + logo: "Logo (640x130)" #FIXME + logo_mobile: "Mobile logo (75x26)" #FIXME + logo_mobile_svg: "Mobile logo (SVG)" #FIXME home_hero: "Hero image" home_show_stats: "Show statistics" - footer_logo: "Logo (220x76)" + footer_logo: "Logo (220x76)" #FIXME footer_facebook_url: "Facebook URL" footer_twitter_url: "Twitter URL" footer_instagram_url: "Instagram URL" @@ -338,7 +338,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_cart_empty: "Cart empty" products_edit_cart: "Edit your cart" products_from: from - products_change: "No changes to save" + products_change: "No changes to save." products_update_error: "Saving failed with the following error(s):" products_update_error_msg: "Saving failed." products_update_error_data: "Save failed due to invalid data:" @@ -451,16 +451,17 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_variant: Variant products_quantity: Quantity products_availabel: Available? + products_producer: "Producer" products_price: "Price" products_sku: "SKU" products_name: "name" products_unit: "unit" products_on_hand: "on hand" products_on_demand: "on demand" - products_category: "category" + products_category: "Category" products_tax_category: "tax category" - products_available_on: "available on" - products_inherits_properties: "Inherits Properties ?" + products_available_on: "Available On" + products_inherits_properties: "Inherits Properties?" register_title: Register From 5e081ce3ee5c64f03784d1dc20b5d9c6c0e41a12 Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Tue, 15 Dec 2015 22:40:21 +0100 Subject: [PATCH 017/215] Translations of validation messages in app/models --- app/models/customer.rb | 3 ++- app/models/enterprise_relationship.rb | 2 +- app/models/spree/product_decorator.rb | 4 ++-- config/locales/en.yml | 5 +++++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index 856f7e94d7..1ab2f3ae5c 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -5,7 +5,7 @@ class Customer < ActiveRecord::Base belongs_to :user, :class_name => Spree.user_class validates :code, uniqueness: { scope: :enterprise_id, allow_blank: true, allow_nil: true } - validates :email, presence: true, uniqueness: { scope: :enterprise_id, message: "is associated with an existing customer" } + validates :email, presence: true, uniqueness: { scope: :enterprise_id, message: I18n.t('validation_msg_is_associated_with_an_exising_customer') } validates :enterprise_id, presence: true scope :of, ->(enterprise) { where(enterprise_id: enterprise) } @@ -18,3 +18,4 @@ class Customer < ActiveRecord::Base self.user = user || Spree::User.find_by_email(email) end end + diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index 8d279b2a20..f32f7faae9 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -4,7 +4,7 @@ class EnterpriseRelationship < ActiveRecord::Base has_many :permissions, class_name: 'EnterpriseRelationshipPermission', dependent: :destroy validates_presence_of :parent_id, :child_id - validates_uniqueness_of :child_id, scope: :parent_id, message: "^That relationship is already established." + validates_uniqueness_of :child_id, scope: :parent_id, message: I18n.t('validation_msg_relationship_already_established') scope :with_enterprises, joins('LEFT JOIN enterprises AS parent_enterprises ON parent_enterprises.id = enterprise_relationships.parent_id'). diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index b089c9a52b..309111fd8b 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -26,8 +26,8 @@ Spree::Product.class_eval do # 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" + validates :primary_taxon, presence: { message: I18n.t("validation_msg_product_category_cant_be_blank") } + validates :tax_category_id, presence: { message: I18n.t("validation_msg_tax") }, if: "Spree::Config.products_require_tax_category" validates_presence_of :variant_unit validates_presence_of :variant_unit_scale, diff --git a/config/locales/en.yml b/config/locales/en.yml index ef8da66414..8d75094e1e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -894,3 +894,8 @@ Please follow the instructions there to make your enterprise visible on the Open payment_methods: "Payment Methods" enterprise_fees: "Enterprise Fees" shop_preferences: "Shop Preferences" + validation_msg_relationship_already_established: "^That relationship is already established." + validation_msg_at_least_one_hub: "^At least one hub must be selected" + validation_msg_product_category_cant_be_blank: "^Product Category cant be blank" + validation_msg_tax_category_cant_be_blank: "^Tax Category can't be blank" + validation_msg_is_associated_with_an_exising_customer: "is associated with an existing customer" From 7e50cc9ce844375dae14d79a46f5211301ed1267 Mon Sep 17 00:00:00 2001 From: stveep Date: Mon, 21 Dec 2015 14:12:38 +0000 Subject: [PATCH 018/215] Data organisation in angular, basic table structure and balance calculation. --- .../distributor_node_controller.js.coffee | 7 +++-- .../darkswarm/services/orders.js.coffee | 15 ++++++---- app/helpers/injection_helper.rb | 3 +- app/serializers/api/order_serializer.rb | 28 ++++++++++++++++++- app/views/spree/users/_fat.html.haml | 21 ++++++++++++-- app/views/spree/users/show.html.haml | 7 +++-- 6 files changed, 66 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee index a260f81951..7c373326f2 100644 --- a/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee @@ -1,8 +1,9 @@ Darkswarm.controller "DistributorNodeCtrl", ($scope, HashNavigation, $anchorScroll) -> $scope.toggle = -> - HashNavigation.toggle $scope.distributor - $scope.open = -> - HashNavigation.active($scope.distributor) + HashNavigation.toggle $scope.distributor_id + $scope.open = -> + HashNavigation.active($scope.distributor_id) + if $scope.open() $anchorScroll() diff --git a/app/assets/javascripts/darkswarm/services/orders.js.coffee b/app/assets/javascripts/darkswarm/services/orders.js.coffee index dbfcf207e3..549e4f8a38 100644 --- a/app/assets/javascripts/darkswarm/services/orders.js.coffee +++ b/app/assets/javascripts/darkswarm/services/orders.js.coffee @@ -1,14 +1,19 @@ Darkswarm.factory 'Orders', (orders, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope)-> new class Orders orders_by_distributor: {} - distributors = [] + distributor_names_by_id: {} constructor: -> # Populate Orders.orders from json in page. @orders = orders # Organise orders by distributor. for order in orders - if order.distributor?.id - @orders_by_distributor[order.distributor.name] = order - # Can we guarantee order of keys in js? - @distributors = Object.keys(@orders_by_distributor) + if order.distributor? + if @orders_by_distributor[order.distributor.id]? then @orders_by_distributor[order.distributor.id].push order else @orders_by_distributor[order.distributor.id] = [order] + if !@distributor_names_by_id[order.distributor.id] then @distributor_names_by_id[order.distributor.id] = {name: order.distributor.name, balance: 0} + for id in Object.keys(@distributor_names_by_id) + @distributor_names_by_id[id].balance = @orders_by_distributor[id].reduce(((x,y) -> + x + Number(y.outstanding_balance)), 0).toFixed(2) + console.log @distributor_names_by_id + + # Sorting by most orders (most recent/frequent?) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 129c9aeed2..0354bcdeab 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -52,7 +52,8 @@ module InjectionHelper end def inject_orders_for_user - inject_json_ams "orders", spree_current_user.orders, Api::OrderSerializer + # Convert ActiveRecord::Relation to array for serialization + inject_json_ams "orders", spree_current_user.orders.where(state: :complete).to_a, Api::OrderSerializer end def inject_json(name, partial, opts = {}) diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index 6648cb7cea..d7fb8e6b6d 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -1,10 +1,36 @@ class Api::OrderSerializer < ActiveModel::Serializer - attributes :id, :completed_at, :total, :state, :shipment_state, :outstanding_balance + attributes :id, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :total_money, :balance_money has_one :distributor, serializer: Api::IdNameSerializer + def completed_at object.completed_at.blank? ? "" : object.completed_at.strftime("%F %T") end + def total_money + to_money(object.total) + end + + def shipment_state + object.shipment_state ? object.shipment_state.humanize : nil # Or a call to t() here? + end + + def payment_state + object.payment_state ? object.payment_state.humanize : nil # Or a call to t() here? + end + + def state + object.state ? object.state.humanize : nil # Or a call to t() here? + end + + def balance_money + to_money(object.outstanding_balance) + end + + private + + def to_money(amount) + {currency_symbol:amount.to_money.currency_symbol, amount:amount.to_money.to_s} + end end diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index ce16555492..d3e2030408 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -1,3 +1,20 @@ -.row.active_table_row +.row.active_table_row{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} + .container{"bo-text" => "orders[0].distributor.name"} + %span {{orders[0].total_money.currency_symbol}} {{Orders.distributor_names_by_id[distributor_id].balance}} .columns.small-12.medium-7.large-7.fat - %span{"bo-text" => "distributor"} + %table + %th + %tr + %td Transaction + %td Date + %td Payment Status + %td Shipping Status + %td Value + %td Balance + %tr{"ng-repeat" => "order in orders"} + %td {{order.id}} + %td {{order.completed_at}} + %td {{order.payment_state}} + %td {{order.shipment_state}} + %td {{order.total_money.currency_symbol}} {{order.total_money.amount}} + %td {{order.balance_money.currency_symbol}} {{order.balance_money.amount}} diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index 733c7aa6aa..55ccf737a0 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -14,9 +14,10 @@ .row{bindonce: true} .small-12.columns .active_table - %distributor.active_table_node.row.animate-repeat{id: "{{distributor}}", - "ng-repeat" => "distributor in Orders.distributors", + %distributor.active_table_node.row.animate-repeat{"ng-repeat" => "(distributor_id, orders) in Orders.orders_by_distributor", "ng-controller" => "DistributorNodeCtrl", - "ng-class" => "{'closed' : !open(), 'open' : open()}"} + "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !distributor.active}", + id: "{{distributor_id}}"} .small-12.columns = render partial: "spree/users/fat" + -# TODO: Skinny partial From 90e627ad60b074109bf87ee62a534d00568cf83f Mon Sep 17 00:00:00 2001 From: stveep Date: Mon, 21 Dec 2015 17:40:36 +0000 Subject: [PATCH 019/215] Data manipulation and balance calc in rails instead --- app/helpers/injection_helper.rb | 7 +++++++ app/models/spree/user_decorator.rb | 6 ++++++ app/serializers/api/order_serializer.rb | 3 --- app/serializers/api/orders_by_distributor_serializer.rb | 8 ++++++++ app/views/spree/users/show.html.haml | 1 + 5 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 app/serializers/api/orders_by_distributor_serializer.rb diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 0354bcdeab..e5c311b7a3 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -56,6 +56,13 @@ module InjectionHelper inject_json_ams "orders", spree_current_user.orders.where(state: :complete).to_a, Api::OrderSerializer end + def inject_orders_by_distributor + #render partial: "json/injection_ams", locals: {name:"orders_by_distributor", json: spree_current_user.orders_by_distributor} + inject_json_ams "orders_by_distributor", + Enterprise.includes(:distributed_orders).where(id: spree_current_user.enterprises_ordered_from).to_a, + Api::OrdersByDistributorSerializer + end + def inject_json(name, partial, opts = {}) render partial: "json/injection", locals: {name: name, partial: partial}.merge(opts) end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index b724a41d1b..aa61e63e33 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -49,6 +49,12 @@ Spree.user_class.class_eval do owned_enterprises(:reload).size < enterprise_limit end + # Returns Enterprise IDs for distributors that the user has shopped at + def enterprises_ordered_from + self.orders.where(state: :complete).map(&:distributor_id).uniq + end + + private def limit_owned_enterprises diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index d7fb8e6b6d..fb1c3f28d3 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -1,9 +1,6 @@ class Api::OrderSerializer < ActiveModel::Serializer attributes :id, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :total_money, :balance_money - has_one :distributor, serializer: Api::IdNameSerializer - - def completed_at object.completed_at.blank? ? "" : object.completed_at.strftime("%F %T") end diff --git a/app/serializers/api/orders_by_distributor_serializer.rb b/app/serializers/api/orders_by_distributor_serializer.rb new file mode 100644 index 0000000000..35315a5cc9 --- /dev/null +++ b/app/serializers/api/orders_by_distributor_serializer.rb @@ -0,0 +1,8 @@ +class Api::OrdersByDistributorSerializer < ActiveModel::Serializer + attributes :name, :balance, :distributed_orders + has_many :distributed_orders, serializer: Api::OrderSerializer + + def balance + object.distributed_orders.map(&:outstanding_balance).reduce(:+).to_money.to_s + end +end diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index 55ccf737a0..a9e82ebfce 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -1,4 +1,5 @@ = inject_orders_for_user += inject_orders_by_distributor .row.pad-top .small-12.columns.pad-top From da0810382b64681815e6d3011b7214bdc73151af Mon Sep 17 00:00:00 2001 From: stveep Date: Mon, 21 Dec 2015 22:19:00 +0000 Subject: [PATCH 020/215] Sorting by most orders, currency symbol, hide until clicked, reformatting table headers --- .../distributor_node_controller.js.coffee | 6 ++-- .../darkswarm/services/orders.js.coffee | 18 ++--------- app/helpers/injection_helper.rb | 13 +++----- .../api/orders_by_distributor_serializer.rb | 2 +- app/views/producers/_skinny.html.haml | 10 +++--- app/views/spree/users/_fat.html.haml | 31 +++++++++---------- app/views/spree/users/_skinny.html.haml | 16 ++++------ app/views/spree/users/show.html.haml | 11 +++---- 8 files changed, 41 insertions(+), 66 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee index 7c373326f2..2c81833e2e 100644 --- a/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee @@ -1,9 +1,9 @@ Darkswarm.controller "DistributorNodeCtrl", ($scope, HashNavigation, $anchorScroll) -> $scope.toggle = -> - HashNavigation.toggle $scope.distributor_id - + HashNavigation.toggle $scope.distributor.id + $scope.open = -> - HashNavigation.active($scope.distributor_id) + HashNavigation.active($scope.distributor.id) if $scope.open() $anchorScroll() diff --git a/app/assets/javascripts/darkswarm/services/orders.js.coffee b/app/assets/javascripts/darkswarm/services/orders.js.coffee index 549e4f8a38..3cb65278aa 100644 --- a/app/assets/javascripts/darkswarm/services/orders.js.coffee +++ b/app/assets/javascripts/darkswarm/services/orders.js.coffee @@ -1,19 +1,7 @@ -Darkswarm.factory 'Orders', (orders, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope)-> +Darkswarm.factory 'Orders', (orders_by_distributor, currencyConfig, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope)-> new class Orders - orders_by_distributor: {} - distributor_names_by_id: {} constructor: -> # Populate Orders.orders from json in page. - @orders = orders - # Organise orders by distributor. - for order in orders - if order.distributor? - if @orders_by_distributor[order.distributor.id]? then @orders_by_distributor[order.distributor.id].push order else @orders_by_distributor[order.distributor.id] = [order] - if !@distributor_names_by_id[order.distributor.id] then @distributor_names_by_id[order.distributor.id] = {name: order.distributor.name, balance: 0} - for id in Object.keys(@distributor_names_by_id) - @distributor_names_by_id[id].balance = @orders_by_distributor[id].reduce(((x,y) -> - x + Number(y.outstanding_balance)), 0).toFixed(2) - console.log @distributor_names_by_id - - + @orders_by_distributor = orders_by_distributor + @currency_symbol = currencyConfig.symbol # Sorting by most orders (most recent/frequent?) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index e5c311b7a3..28d896e6a0 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -51,16 +51,11 @@ module InjectionHelper render partial: "json/injection_ams", locals: {name: 'enterpriseAttributes', json: "#{@enterprise_attributes.to_json}"} end - def inject_orders_for_user - # Convert ActiveRecord::Relation to array for serialization - inject_json_ams "orders", spree_current_user.orders.where(state: :complete).to_a, Api::OrderSerializer - end - def inject_orders_by_distributor - #render partial: "json/injection_ams", locals: {name:"orders_by_distributor", json: spree_current_user.orders_by_distributor} - inject_json_ams "orders_by_distributor", - Enterprise.includes(:distributed_orders).where(id: spree_current_user.enterprises_ordered_from).to_a, - Api::OrdersByDistributorSerializer + # Convert ActiveRecord::Relation to array for serialization + data_array = Enterprise.includes(:distributed_orders).where(enterprises: {id: spree_current_user.enterprises_ordered_from }, spree_orders: {state: :complete, user_id: spree_current_user.id}).to_a + data_array.sort!{|a,b| b.distributed_orders.length <=> a.distributed_orders.length} # Better to do in SQL/Angular? + inject_json_ams "orders_by_distributor", data_array, Api::OrdersByDistributorSerializer end def inject_json(name, partial, opts = {}) diff --git a/app/serializers/api/orders_by_distributor_serializer.rb b/app/serializers/api/orders_by_distributor_serializer.rb index 35315a5cc9..a333ff6ff4 100644 --- a/app/serializers/api/orders_by_distributor_serializer.rb +++ b/app/serializers/api/orders_by_distributor_serializer.rb @@ -1,5 +1,5 @@ class Api::OrdersByDistributorSerializer < ActiveModel::Serializer - attributes :name, :balance, :distributed_orders + attributes :name, :id, :balance, :distributed_orders has_many :distributed_orders, serializer: Api::OrderSerializer def balance diff --git a/app/views/producers/_skinny.html.haml b/app/views/producers/_skinny.html.haml index 11860d35d6..cf066be05a 100644 --- a/app/views/producers/_skinny.html.haml +++ b/app/views/producers/_skinny.html.haml @@ -1,20 +1,20 @@ .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" } + %a.is_distributor{"bo-href" => "producer.path" } %i{bo: {class: "producer.producer_icon_font"}} - %span.margin-top + %span.margin-top %strong{"bo-text" => "producer.name"} %span.producer-name{"bo-if" => "!producer.is_distributor" } %i{bo: {class: "producer.producer_icon_font"}} - %span.margin-top + %span.margin-top %strong{"bo-text" => "producer.name"} - + .columns.small-6.medium-3.large-3 %span.margin-top{"bo-text" => "producer.address.city"} .columns.small-4.medium-3.large-4 %span.margin-top{"bo-bind" => "producer.address.state_name | uppercase"} .columns.small-2.medium-2.large-1.text-right - %span.margin-top + %span.margin-top %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index d3e2030408..a447ceb859 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -1,20 +1,17 @@ -.row.active_table_row{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} - .container{"bo-text" => "orders[0].distributor.name"} - %span {{orders[0].total_money.currency_symbol}} {{Orders.distributor_names_by_id[distributor_id].balance}} +.row.active_table_row{"ng-if" => "open()","ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} .columns.small-12.medium-7.large-7.fat %table - %th - %tr - %td Transaction - %td Date - %td Payment Status - %td Shipping Status - %td Value - %td Balance - %tr{"ng-repeat" => "order in orders"} - %td {{order.id}} - %td {{order.completed_at}} - %td {{order.payment_state}} - %td {{order.shipment_state}} + %tr + %th Transaction + %th Date + %th Payment Status + %th Shipping Status + %th Value + %th Balance + %tr{"ng-repeat" => "order in distributor.distributed_orders"} + %td{"bo-text" => "order.id"} + %td{"bo-text" => "order.completed_at"} + %td{"bo-text" => "order.payment_state"} + %td{"bo-text" => "order.shipment_state"} %td {{order.total_money.currency_symbol}} {{order.total_money.amount}} - %td {{order.balance_money.currency_symbol}} {{order.balance_money.amount}} + %td{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} {{order.balance_money.currency_symbol}} {{order.balance_money.amount}} diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index b608bfcf98..7f3e0e5135 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -1,14 +1,10 @@ --# Add extra highlighting etc (credit/debit?) here .row.active_table_row{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} .columns.small-12.medium-4.large-4.skinny-head %span.margin-top - %strong{"bo-text" => "distributor"} + %strong{"bo-text" => "distributor.name"} - -# - .columns.small-6.medium-3.large-3 - %span.margin-top{"bo-text" => "producer.address.city"} - .columns.small-4.medium-3.large-4 - %span.margin-top{"bo-bind" => "producer.address.state_name | uppercase"} - .columns.small-2.medium-2.large-1.text-right - %span.margin-top - %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} + .columns.small-6.medium-3.large-3 + %span.margin-top{"bo-text" => "'Balance: ' + Orders.currency_symbol + ' ' + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} + .columns.small-2.medium-2.large-1.text-right + %span.margin-top + %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index a9e82ebfce..d0a7ed495c 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -1,4 +1,3 @@ -= inject_orders_for_user = inject_orders_by_distributor .row.pad-top @@ -10,15 +9,15 @@ %dd = @user.email (#{link_to t(:edit), spree.edit_account_path}) - -# Add back ng-cloak below - .orders{"ng-controller" => "OrdersCtrl"} + %h3 My Orders + .orders{"ng-controller" => "OrdersCtrl", "ng-cloak" => true} .row{bindonce: true} .small-12.columns .active_table - %distributor.active_table_node.row.animate-repeat{"ng-repeat" => "(distributor_id, orders) in Orders.orders_by_distributor", + %distributor.active_table_node.row.animate-repeat{"ng-repeat" => "(key, distributor) in Orders.orders_by_distributor", "ng-controller" => "DistributorNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !distributor.active}", - id: "{{distributor_id}}"} + id: "{{distributor.id}}"} .small-12.columns + = render partial: "spree/users/skinny" = render partial: "spree/users/fat" - -# TODO: Skinny partial From 668c6ff74f88a0177bb3ec377a7f7f214c75d31c Mon Sep 17 00:00:00 2001 From: stveep Date: Mon, 21 Dec 2015 23:22:15 +0000 Subject: [PATCH 021/215] Order serializer spec --- spec/serializers/order_serializer_spec.rb | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 spec/serializers/order_serializer_spec.rb diff --git a/spec/serializers/order_serializer_spec.rb b/spec/serializers/order_serializer_spec.rb new file mode 100644 index 0000000000..7b71788f17 --- /dev/null +++ b/spec/serializers/order_serializer_spec.rb @@ -0,0 +1,32 @@ +#require 'spec_helper' + +describe Api::OrderSerializer do + let(:serializer) { Api::OrderSerializer.new order } + let(:order) { create(:completed_order_with_totals) } + + + it "serializes an order" do + expect(serializer.to_json).to match order.id.to_s + end + + it "converts the total to currency and amount" do + puts order.inspect + puts serializer.serializable_hash.inspect + expect(serializer.serializable_hash[:total_money].keys).to include :currency_symbol + # Not sure what currency symbol is in test env + expect(serializer.serializable_hash[:total_money].keys).to include :amount + expect(serializer.serializable_hash[:total_money][:amount]).to eq "0.00" + end + + it "converts the balance to currency and amount" do + expect(serializer.serializable_hash[:balance_money].keys).to include :currency_symbol + # Not sure what currency symbol is in test env + expect(serializer.serializable_hash[:balance_money].keys).to include :amount + expect(serializer.serializable_hash[:balance_money][:amount]).to eq "0.00" + end + it "convert the state attributes to readable strings" do + expect(serializer.to_json).to match "Complete" + expect(serializer.to_json).to match "Balance due" + end + +end From 48896ab3d88cbca6d0c147d113c0523f024aa0da Mon Sep 17 00:00:00 2001 From: stveep Date: Tue, 22 Dec 2015 15:09:24 +0000 Subject: [PATCH 022/215] More serializer specs --- app/helpers/injection_helper.rb | 3 +- spec/helpers/injection_helper_spec.rb | 11 +++++--- spec/serializers/order_serializer_spec.rb | 2 -- .../orders_by_distributor_serializer_spec.rb | 28 +++++++++++++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 spec/serializers/orders_by_distributor_serializer_spec.rb diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 28d896e6a0..34c991d55d 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -53,8 +53,9 @@ module InjectionHelper def inject_orders_by_distributor # Convert ActiveRecord::Relation to array for serialization + # This query could maybe go in a model, or just serialize orders and handle the rest in JS data_array = Enterprise.includes(:distributed_orders).where(enterprises: {id: spree_current_user.enterprises_ordered_from }, spree_orders: {state: :complete, user_id: spree_current_user.id}).to_a - data_array.sort!{|a,b| b.distributed_orders.length <=> a.distributed_orders.length} # Better to do in SQL/Angular? + data_array.sort!{|a,b| b.distributed_orders.length <=> a.distributed_orders.length} inject_json_ams "orders_by_distributor", data_array, Api::OrdersByDistributorSerializer end diff --git a/spec/helpers/injection_helper_spec.rb b/spec/helpers/injection_helper_spec.rb index 362ca6479b..0eecdeeaf9 100644 --- a/spec/helpers/injection_helper_spec.rb +++ b/spec/helpers/injection_helper_spec.rb @@ -3,6 +3,13 @@ require 'spec_helper' describe InjectionHelper do let!(:enterprise) { create(:distributor_enterprise, facebook: "roger") } + let!(:distributor1) { create(:distributor_enterprise) } + let!(:distributor2) { create(:distributor_enterprise) } + let!(:user) { create(:user)} + let!(:d1o1) { create(:completed_order_with_totals, distributor: distributor1, user_id: user.id, total: 10000)} + let!(:d1o2) { create(:completed_order_with_totals, distributor: distributor1, user_id: user.id, total: 5000)} + let!(:d2o1) { create(:completed_order_with_totals, distributor: distributor2, user_id: user.id)} + it "will inject via AMS" do helper.inject_json_ams("test", [enterprise], Api::IdSerializer).should match /#{enterprise.id}/ end @@ -42,8 +49,4 @@ describe InjectionHelper do helper.inject_taxons.should match taxon.name end - it "injects taxons" do - taxon = create(:taxon) - helper.inject_taxons.should match taxon.name - end end diff --git a/spec/serializers/order_serializer_spec.rb b/spec/serializers/order_serializer_spec.rb index 7b71788f17..d3d8c98dd1 100644 --- a/spec/serializers/order_serializer_spec.rb +++ b/spec/serializers/order_serializer_spec.rb @@ -10,8 +10,6 @@ describe Api::OrderSerializer do end it "converts the total to currency and amount" do - puts order.inspect - puts serializer.serializable_hash.inspect expect(serializer.serializable_hash[:total_money].keys).to include :currency_symbol # Not sure what currency symbol is in test env expect(serializer.serializable_hash[:total_money].keys).to include :amount diff --git a/spec/serializers/orders_by_distributor_serializer_spec.rb b/spec/serializers/orders_by_distributor_serializer_spec.rb new file mode 100644 index 0000000000..4a75f76fea --- /dev/null +++ b/spec/serializers/orders_by_distributor_serializer_spec.rb @@ -0,0 +1,28 @@ +#require 'spec_helper' + +describe Api::OrdersByDistributorSerializer do + + # Banged lets ensure entered into test database + let!(:distributor1) { create(:distributor_enterprise) } + let!(:distributor2) { create(:distributor_enterprise) } + let!(:user) { create(:user)} + let!(:d1o1) { create(:completed_order_with_totals, distributor: distributor1, user_id: user.id, total: 10000)} + let!(:d1o2) { create(:completed_order_with_totals, distributor: distributor1, user_id: user.id, total: 5000)} + let!(:d2o1) { create(:completed_order_with_totals, distributor: distributor2, user_id: user.id)} + + before do + @data = Enterprise.includes(:distributed_orders).where(enterprises: {id: user.enterprises_ordered_from }, spree_orders: {state: :complete, user_id: user.id}).to_a + @serializer = ActiveModel::ArraySerializer.new(@data, {each_serializer: Api::OrdersByDistributorSerializer}) + end + + it "serializes orders" do + expect(@serializer.to_json).to match "distributed_orders" + end + + it "serializes the balance for each distributor" do + expect(@serializer.serializable_array[0].keys).to include :balance + # Would be good to test adding up balance properly but can't get a non-zero total from the factories... + expect(@serializer.serializable_array[0][:balance]).to eq "0.00" + end + +end From 59adf88cade6ac5666bf693257f6ca55a0b18f9d Mon Sep 17 00:00:00 2001 From: stveep Date: Tue, 22 Dec 2015 22:59:07 +0000 Subject: [PATCH 023/215] Feature spec --- app/helpers/injection_helper.rb | 2 +- app/views/spree/users/show.html.haml | 45 +++++++++++++------------- spec/features/consumer/account_spec.rb | 38 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 spec/features/consumer/account_spec.rb diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 34c991d55d..b07fdaea07 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -2,7 +2,7 @@ require 'open_food_network/enterprise_injection_data' module InjectionHelper def inject_enterprises - inject_json_ams "enterprises", Enterprise.activated.includes(:address).all, Api::EnterpriseSerializer, enterprise_injection_data + inject_json_ams "enterprises", Enterprise.activated.includes(address: :state).all, Api::EnterpriseSerializer, enterprise_injection_data end def inject_group_enterprises diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index d0a7ed495c..a19c87dd21 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -1,23 +1,24 @@ -= inject_orders_by_distributor +.darkswarm + = inject_orders_by_distributor -.row.pad-top - .small-12.columns.pad-top - %h1= accurate_title - .account-summary{"data-hook" => "account_summary"} - %dl#user-info - %dt= t(:email) - %dd - = @user.email - (#{link_to t(:edit), spree.edit_account_path}) - %h3 My Orders - .orders{"ng-controller" => "OrdersCtrl", "ng-cloak" => true} - .row{bindonce: true} - .small-12.columns - .active_table - %distributor.active_table_node.row.animate-repeat{"ng-repeat" => "(key, distributor) in Orders.orders_by_distributor", - "ng-controller" => "DistributorNodeCtrl", - "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !distributor.active}", - id: "{{distributor.id}}"} - .small-12.columns - = render partial: "spree/users/skinny" - = render partial: "spree/users/fat" + .row.pad-top + .small-12.columns.pad-top + %h1= accurate_title + .account-summary{"data-hook" => "account_summary"} + %dl#user-info + %dt= t(:email) + %dd + = @user.email + (#{link_to t(:edit), spree.edit_account_path}) + %h3 My Orders + .orders{"ng-controller" => "OrdersCtrl", "ng-cloak" => true} + .row{bindonce: true} + .small-12.columns + .active_table + %distributor.active_table_node.row.animate-repeat{"ng-repeat" => "(key, distributor) in Orders.orders_by_distributor", + "ng-controller" => "DistributorNodeCtrl", + "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !distributor.active}", + id: "{{distributor.id}}"} + .small-12.columns + = render partial: "spree/users/skinny" + = render partial: "spree/users/fat" diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb new file mode 100644 index 0000000000..7c705dd612 --- /dev/null +++ b/spec/features/consumer/account_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +feature %q{ + As a consumer + I want to view my order history with each hub + and view any outstanding balance. +}, js: true do + include UIComponentHelper + include AuthenticationWorkflow + let!(:user) { create(:user)} + let!(:distributor1) { create(:distributor_enterprise) } + let!(:distributor2) { create(:distributor_enterprise) } + let!(:distributor_without_orders) { create(:distributor_enterprise) } + let!(:d1o1) { create(:completed_order_with_totals, distributor: distributor1, user_id: user.id, total: 10000)} + let!(:d1o2) { create(:completed_order_with_totals, distributor: distributor1, user_id: user.id, total: 5000)} + let!(:d2o1) { create(:completed_order_with_totals, distributor: distributor2, user_id: user.id)} + + let!(:d1o1p) { create(:payment, order: d1o1)} + + before do + login_as user + visit "/account" + end + + it "shows all hubs that have been ordered from" do + expect(page).to have_content distributor1.name + expect(page).to have_content distributor2.name + expect(page).not_to have_content distributor_without_orders.name + end + + it "reveals table of orders for distributors when clicked" do + expand_active_table_node distributor1.name + expect(page).to have_content d1o1.id + expand_active_table_node distributor2.name + expect(page).not_to have_content d1o1.id + end + +end From f9435a3c34224fba0b6aee901f131568379eae8e Mon Sep 17 00:00:00 2001 From: stveep Date: Wed, 23 Dec 2015 14:39:56 +0000 Subject: [PATCH 024/215] Starting i18n --- app/views/spree/users/_fat.html.haml | 16 ++++++++-------- app/views/spree/users/_skinny.html.haml | 2 +- app/views/spree/users/show.html.haml | 2 +- config/locales/en-GB.yml | 11 ++++++----- config/locales/en.yml | 9 +++++++++ 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index a447ceb859..be349c38ef 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -2,16 +2,16 @@ .columns.small-12.medium-7.large-7.fat %table %tr - %th Transaction - %th Date - %th Payment Status - %th Shipping Status - %th Value - %th Balance + %th{"bo-text" => "'transaction' | t"} + %th{"bo-text" => "'transaction_date' | t"} + %th{"bo-text" => "'payment_state' | t"} + %th{"bo-text" => "'shipping_state' | t"} + %th{"bo-text" => "'value' | t"} + %th{"bo-text" => "'balance' | t"} %tr{"ng-repeat" => "order in distributor.distributed_orders"} %td{"bo-text" => "order.id"} %td{"bo-text" => "order.completed_at"} - %td{"bo-text" => "order.payment_state"} - %td{"bo-text" => "order.shipment_state"} + %td{"bo-text" => "order.payment_state | t"} + %td{"bo-text" => "order.shipment_state | t"} %td {{order.total_money.currency_symbol}} {{order.total_money.amount}} %td{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} {{order.balance_money.currency_symbol}} {{order.balance_money.amount}} diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index 7f3e0e5135..8ad8c596ce 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -4,7 +4,7 @@ %strong{"bo-text" => "distributor.name"} .columns.small-6.medium-3.large-3 - %span.margin-top{"bo-text" => "'Balance: ' + Orders.currency_symbol + ' ' + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} + %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + ' ' + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} .columns.small-2.medium-2.large-1.text-right %span.margin-top %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index a19c87dd21..d17eed637b 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -10,7 +10,7 @@ %dd = @user.email (#{link_to t(:edit), spree.edit_account_path}) - %h3 My Orders + %h3= t(:my_orders) .orders{"ng-controller" => "OrdersCtrl", "ng-cloak" => true} .row{bindonce: true} .small-12.columns diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index e21573f1b7..e605bf2f44 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -344,19 +344,19 @@ See the %{link} to find out more about %{sitename}'s features and to start using login_invalid: "Invalid email or password" modal_hubs: "Food Hubs" - modal_hubs_abstract: Our food hubs are the point of contact between you and the people who make your food! - modal_hubs_content1: You can search for a convenient hub by location or name. Some hubs have multiple points where you can pick-up your purchases, and some will also provide delivery options. Each food hub is a sales point with independent business operations and logistics - so variations between hubs are to be expected. + modal_hubs_abstract: Our food hubs are the point of contact between you and the people who make your food! + modal_hubs_content1: You can search for a convenient hub by location or name. Some hubs have multiple points where you can pick-up your purchases, and some will also provide delivery options. Each food hub is a sales point with independent business operations and logistics - so variations between hubs are to be expected. modal_hubs_content2: You can only shop at one food hub at a time. modal_groups: "Groups / Regions" - modal_groups_content1: These are the organisations and relationships between hubs which make up the Open Food Network. - modal_groups_content2: Some groups are clustered by location or council, others by non-geographic similarities. + modal_groups_content1: These are the organisations and relationships between hubs which make up the Open Food Network. + modal_groups_content2: Some groups are clustered by location or council, others by non-geographic similarities. modal_how: "How it works" modal_how_shop: Shop the Open Food Network modal_how_shop_explained: Search for a food hub near you to start shopping! You can expand each food hub to see what kinds of goodies are available, and click through to start shopping. (You can only shop one food hub at a time.) modal_how_pickup: Pick-ups, delivery & shipping costs - modal_how_pickup_explained: Some food hubs deliver to your door, while others require you to pick-up your purchases. You can see which options are available on the homepage, and select which you'd like at the shopping and check-out pages. Delivery will cost more, and pricing differs from hub-to-hub. Each food hub is a sales point with independent business operations and logisitics - so variations between hubs are to be expected. + modal_how_pickup_explained: Some food hubs deliver to your door, while others require you to pick-up your purchases. You can see which options are available on the homepage, and select which you'd like at the shopping and check-out pages. Delivery will cost more, and pricing differs from hub-to-hub. Each food hub is a sales point with independent business operations and logisitics - so variations between hubs are to be expected. modal_how_more: Learn more modal_how_more_explained: "If you want to learn more about the Open Food Network, how it works, and get involved, check out:" @@ -635,3 +635,4 @@ Please follow the instructions there to make your enterprise visible on the Open price_graph: "Price graph" included_tax: "Included tax" remove_tax: "Remove tax" + balance: "Balanceroos" diff --git a/config/locales/en.yml b/config/locales/en.yml index c9df13af92..0849c710a0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -658,3 +658,12 @@ Please follow the instructions there to make your enterprise visible on the Open price_graph: "Price graph" included_tax: "Included tax" remove_tax: "Remove tax" + balance: "Balance" + transaction: "Transaction" + transaction_date: "Date" #Transaction is only in key to avoid conflict with :date + payment_state: "Payment status" + shipping_state: "Shipping status" + value: "Value" + "Balance due": "Balance due" # TODO: In /accounts better to pass the symbol and translate rather than humanize + Paid: "Paid" + Ready: "Ready" From 6992d6620286c007de87a4044535d93e8a08ea36 Mon Sep 17 00:00:00 2001 From: stveep Date: Fri, 1 Jan 2016 09:37:21 -0500 Subject: [PATCH 025/215] Add payments data, reformat dates --- app/serializers/api/order_serializer.rb | 6 ++++-- app/serializers/api/payment_serializer.rb | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 app/serializers/api/payment_serializer.rb diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index fb1c3f28d3..8c8260a901 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -1,8 +1,10 @@ class Api::OrderSerializer < ActiveModel::Serializer - attributes :id, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :total_money, :balance_money + attributes :id, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :total_money, :balance_money, :payments + + has_many :payments, serializer: Api::PaymentSerializer def completed_at - object.completed_at.blank? ? "" : object.completed_at.strftime("%F %T") + object.completed_at.blank? ? "" : object.completed_at.to_formatted_s(:long_ordinal) end def total_money diff --git a/app/serializers/api/payment_serializer.rb b/app/serializers/api/payment_serializer.rb new file mode 100644 index 0000000000..029143403f --- /dev/null +++ b/app/serializers/api/payment_serializer.rb @@ -0,0 +1,14 @@ +class Api::PaymentSerializer < ActiveModel::Serializer + attributes :identifier, :amount, :updated_at, :payment_method + def payment_method + object.payment_method.name + end + + def amount + object.amount.to_money.to_s + end + + def updated_at + object.updated_at.to_formatted_s(:long_ordinal) + end +end From 698d36180d57d002f2bc8b47daac69dda133433b Mon Sep 17 00:00:00 2001 From: stveep Date: Thu, 7 Jan 2016 13:25:15 +0000 Subject: [PATCH 026/215] Move query from injector to User instance method --- app/helpers/injection_helper.rb | 4 ++-- app/models/spree/user_decorator.rb | 4 ++++ app/views/spree/users/_fat.html.haml | 24 ++++++++++++++++-------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index b07fdaea07..b3fa374607 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -53,8 +53,8 @@ module InjectionHelper def inject_orders_by_distributor # Convert ActiveRecord::Relation to array for serialization - # This query could maybe go in a model, or just serialize orders and handle the rest in JS - data_array = Enterprise.includes(:distributed_orders).where(enterprises: {id: spree_current_user.enterprises_ordered_from }, spree_orders: {state: :complete, user_id: spree_current_user.id}).to_a + data_array = spree_current_user.orders_by_distributor.to_a + data_array.each{|enterprise| enterprise.distributed_orders.each{|order| order.payments.keep_if{|payment| payment.state == "completed"} }} data_array.sort!{|a,b| b.distributed_orders.length <=> a.distributed_orders.length} inject_json_ams "orders_by_distributor", data_array, Api::OrdersByDistributorSerializer end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index aa61e63e33..7a808e6547 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -54,6 +54,10 @@ Spree.user_class.class_eval do self.orders.where(state: :complete).map(&:distributor_id).uniq end + # Returns orders and their associated payments for all distributors that have been ordered from + def orders_by_distributor + Enterprise.includes(distributed_orders: :payments).where(enterprises: {id: self.enterprises_ordered_from }, spree_orders: {state: :complete, user_id: self.id}) + end private diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index be349c38ef..dcaac026c4 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -1,5 +1,5 @@ .row.active_table_row{"ng-if" => "open()","ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} - .columns.small-12.medium-7.large-7.fat + .columns.small-12.medium-12.large-12.fat %table %tr %th{"bo-text" => "'transaction' | t"} @@ -8,10 +8,18 @@ %th{"bo-text" => "'shipping_state' | t"} %th{"bo-text" => "'value' | t"} %th{"bo-text" => "'balance' | t"} - %tr{"ng-repeat" => "order in distributor.distributed_orders"} - %td{"bo-text" => "order.id"} - %td{"bo-text" => "order.completed_at"} - %td{"bo-text" => "order.payment_state | t"} - %td{"bo-text" => "order.shipment_state | t"} - %td {{order.total_money.currency_symbol}} {{order.total_money.amount}} - %td{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} {{order.balance_money.currency_symbol}} {{order.balance_money.amount}} + %tbody{"ng-repeat" => "order in distributor.distributed_orders"} + %tr + %td{"bo-text" => "'Order ' + order.id"} + %td{"bo-text" => "order.completed_at"} + %td{"bo-text" => "order.payment_state | t"} + %td{"bo-text" => "order.shipment_state | t"} + %td.text-right {{order.total_money.currency_symbol}} {{order.total_money.amount}} + %td.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} {{order.balance_money.currency_symbol}} {{order.balance_money.amount}} + %tr{"ng-repeat" => "payment in order.payments"} + %td{"bo-text" => "'Payment '+ payment.identifier"} + %td{"bo-text" => "payment.updated_at"} + %td{"bo-text" => "payment.payment_method"} + %td + %td.text-right {{order.total_money.currency_symbol}} {{payment.amount}} + %td From 262a8e75f967f2efffed3833442ff04b690b52ec Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 10 Jan 2016 07:58:04 +0000 Subject: [PATCH 027/215] slugs --- .../controllers/distributor_node_controller.js.coffee | 4 ++-- app/serializers/api/orders_by_distributor_serializer.rb | 7 ++++++- app/views/spree/users/show.html.haml | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee index 2c81833e2e..6705634771 100644 --- a/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/distributor_node_controller.js.coffee @@ -1,9 +1,9 @@ Darkswarm.controller "DistributorNodeCtrl", ($scope, HashNavigation, $anchorScroll) -> $scope.toggle = -> - HashNavigation.toggle $scope.distributor.id + HashNavigation.toggle $scope.distributor.hash $scope.open = -> - HashNavigation.active($scope.distributor.id) + HashNavigation.active($scope.distributor.hash) if $scope.open() $anchorScroll() diff --git a/app/serializers/api/orders_by_distributor_serializer.rb b/app/serializers/api/orders_by_distributor_serializer.rb index a333ff6ff4..640a8837d7 100644 --- a/app/serializers/api/orders_by_distributor_serializer.rb +++ b/app/serializers/api/orders_by_distributor_serializer.rb @@ -1,8 +1,13 @@ class Api::OrdersByDistributorSerializer < ActiveModel::Serializer - attributes :name, :id, :balance, :distributed_orders + attributes :name, :id, :hash, :balance, :distributed_orders has_many :distributed_orders, serializer: Api::OrderSerializer def balance object.distributed_orders.map(&:outstanding_balance).reduce(:+).to_money.to_s end + + def hash + object.to_param + end + end diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index d17eed637b..2827d64308 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -18,7 +18,7 @@ %distributor.active_table_node.row.animate-repeat{"ng-repeat" => "(key, distributor) in Orders.orders_by_distributor", "ng-controller" => "DistributorNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !distributor.active}", - id: "{{distributor.id}}"} + id: "{{distributor.hash}}"} .small-12.columns = render partial: "spree/users/skinny" = render partial: "spree/users/fat" From f5db1cd9213f3ab5e25b77d0429a98139466e7d9 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 10 Jan 2016 15:53:14 +0000 Subject: [PATCH 028/215] Images, first attempt at column formatting --- .../api/orders_by_distributor_serializer.rb | 6 +++++- app/views/spree/users/_fat.html.haml | 6 +++--- app/views/spree/users/_skinny.html.haml | 18 +++++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/serializers/api/orders_by_distributor_serializer.rb b/app/serializers/api/orders_by_distributor_serializer.rb index 640a8837d7..3d04bf2a03 100644 --- a/app/serializers/api/orders_by_distributor_serializer.rb +++ b/app/serializers/api/orders_by_distributor_serializer.rb @@ -1,5 +1,5 @@ class Api::OrdersByDistributorSerializer < ActiveModel::Serializer - attributes :name, :id, :hash, :balance, :distributed_orders + attributes :name, :id, :hash, :balance, :logo, :distributed_orders has_many :distributed_orders, serializer: Api::OrderSerializer def balance @@ -10,4 +10,8 @@ class Api::OrdersByDistributorSerializer < ActiveModel::Serializer object.to_param end + def logo + object.logo(:small) if object.logo? + end + end diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index dcaac026c4..6ed7a6ad1c 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -10,12 +10,12 @@ %th{"bo-text" => "'balance' | t"} %tbody{"ng-repeat" => "order in distributor.distributed_orders"} %tr - %td{"bo-text" => "'Order ' + order.id"} + %td{"bo-text" => "('order' | t )+ ' ' + order.id"} %td{"bo-text" => "order.completed_at"} %td{"bo-text" => "order.payment_state | t"} %td{"bo-text" => "order.shipment_state | t"} - %td.text-right {{order.total_money.currency_symbol}} {{order.total_money.amount}} - %td.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} {{order.balance_money.currency_symbol}} {{order.balance_money.amount}} + %td.text-right{"bo-text" => "order.total_money.currency_symbol + order.total_money.amount"} + %td.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.balance_money.currency_symbol + order.balance_money.amount"} %tr{"ng-repeat" => "payment in order.payments"} %td{"bo-text" => "'Payment '+ payment.identifier"} %td{"bo-text" => "payment.updated_at"} diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index 8ad8c596ce..64f1582964 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -1,10 +1,14 @@ .row.active_table_row{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} - .columns.small-12.medium-4.large-4.skinny-head + .skinny-head %span.margin-top - %strong{"bo-text" => "distributor.name"} - + .columns.medium-2.large-2 + %span.margin-top + %img{"bo-src" => "distributor.logo"} + .columns.small-10.medium-8.large-8 + %span.margin-top + %strong{"bo-text" => "distributor.name"} + .columns.small-2.medium-2.large-2.text-right + %span.margin-top + %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} .columns.small-6.medium-3.large-3 - %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + ' ' + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} - .columns.small-2.medium-2.large-1.text-right - %span.margin-top - %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} + %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} From 667d1c5428c440cc562dd8477dccace101aca5b8 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 10 Jan 2016 16:16:30 +0000 Subject: [PATCH 029/215] More reliable spec --- spec/features/consumer/account_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb index 7c705dd612..80cf579737 100644 --- a/spec/features/consumer/account_spec.rb +++ b/spec/features/consumer/account_spec.rb @@ -30,9 +30,9 @@ feature %q{ it "reveals table of orders for distributors when clicked" do expand_active_table_node distributor1.name - expect(page).to have_content d1o1.id + expect(page).to have_content "Order " + d1o1.id.to_s expand_active_table_node distributor2.name - expect(page).not_to have_content d1o1.id + expect(page).not_to have_content "Order " + d1o1.id.to_s end end From c9a8d7efa5337f9f3db647c99e09b534ac1d1995 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Fri, 22 Jan 2016 19:49:03 +0000 Subject: [PATCH 030/215] Message for users with no orders --- app/serializers/api/order_serializer.rb | 6 +++++- app/views/spree/users/_fat.html.haml | 3 ++- app/views/spree/users/_skinny.html.haml | 6 +++--- app/views/spree/users/show.html.haml | 3 ++- spec/features/consumer/account_spec.rb | 16 ++++++++++++++-- 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index 8c8260a901..45df13a8aa 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -1,5 +1,5 @@ class Api::OrderSerializer < ActiveModel::Serializer - attributes :id, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :total_money, :balance_money, :payments + attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :total_money, :balance_money, :payments, :path has_many :payments, serializer: Api::PaymentSerializer @@ -27,6 +27,10 @@ class Api::OrderSerializer < ActiveModel::Serializer to_money(object.outstanding_balance) end + def path + spree.order_url(object.number, only_path: true) + end + private def to_money(amount) diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index 6ed7a6ad1c..b50a5e4f94 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -10,7 +10,8 @@ %th{"bo-text" => "'balance' | t"} %tbody{"ng-repeat" => "order in distributor.distributed_orders"} %tr - %td{"bo-text" => "('order' | t )+ ' ' + order.id"} + %td + %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} %td{"bo-text" => "order.completed_at"} %td{"bo-text" => "order.payment_state | t"} %td{"bo-text" => "order.shipment_state | t"} diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index 64f1582964..fcceb9b02e 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -4,11 +4,11 @@ .columns.medium-2.large-2 %span.margin-top %img{"bo-src" => "distributor.logo"} - .columns.small-10.medium-8.large-8 + .columns.small-10.medium-8.large-5 %span.margin-top %strong{"bo-text" => "distributor.name"} + .columns.small-6.medium-3.large-3 + %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} .columns.small-2.medium-2.large-2.text-right %span.margin-top %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} - .columns.small-6.medium-3.large-3 - %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index 2827d64308..290d1ed36a 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -15,10 +15,11 @@ .row{bindonce: true} .small-12.columns .active_table - %distributor.active_table_node.row.animate-repeat{"ng-repeat" => "(key, distributor) in Orders.orders_by_distributor", + %distributor.active_table_node.row.animate-repeat{"ng-if" => "Orders.orders_by_distributor.length > 0", "ng-repeat" => "(key, distributor) in Orders.orders_by_distributor", "ng-controller" => "DistributorNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !distributor.active}", id: "{{distributor.hash}}"} .small-12.columns = render partial: "spree/users/skinny" = render partial: "spree/users/fat" + .message{"ng-if" => "Orders.orders_by_distributor.length == 0", "bo-text" => "'you_have_no_orders_yet' | t"} diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb index 80cf579737..d81da58acc 100644 --- a/spec/features/consumer/account_spec.rb +++ b/spec/features/consumer/account_spec.rb @@ -8,6 +8,7 @@ feature %q{ include UIComponentHelper include AuthenticationWorkflow let!(:user) { create(:user)} + let!(:user2) {create(:user)} let!(:distributor1) { create(:distributor_enterprise) } let!(:distributor2) { create(:distributor_enterprise) } let!(:distributor_without_orders) { create(:distributor_enterprise) } @@ -30,9 +31,20 @@ feature %q{ it "reveals table of orders for distributors when clicked" do expand_active_table_node distributor1.name - expect(page).to have_content "Order " + d1o1.id.to_s + expect(page).to have_content "Order " + d1o1.number.to_s expand_active_table_node distributor2.name - expect(page).not_to have_content "Order " + d1o1.id.to_s + expect(page).not_to have_content "Order " + d1o1.number.to_s + end + + context "for a user without orders" do + before do + login_as user2 + visit "/account" + end + + it "displays an appropriate message" do + expect(page).to have_content "You have no orders yet" + end end end From aa8457c40fafa40b981b48cf861ebf80a5a4c28b Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 26 Jan 2016 19:03:19 +0000 Subject: [PATCH 031/215] Fix order serializer spec: change from id to number --- spec/serializers/order_serializer_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/serializers/order_serializer_spec.rb b/spec/serializers/order_serializer_spec.rb index d3d8c98dd1..f58565de7a 100644 --- a/spec/serializers/order_serializer_spec.rb +++ b/spec/serializers/order_serializer_spec.rb @@ -6,7 +6,7 @@ describe Api::OrderSerializer do it "serializes an order" do - expect(serializer.to_json).to match order.id.to_s + expect(serializer.to_json).to match order.number.to_s end it "converts the total to currency and amount" do From ccd66bba9c3c7f9d07ee8db8f2c6e388d19b7a1a Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Thu, 28 Jan 2016 22:42:45 +0000 Subject: [PATCH 032/215] Credit/debit formatting, use filters for money formatting, use full page width for table --- .../filters/format_balance.js.coffee | 7 ++++ .../darkswarm/services/orders.js.coffee | 1 - app/serializers/api/order_serializer.rb | 16 ++------- app/views/spree/users/_fat.html.haml | 36 +++++++++---------- app/views/spree/users/_skinny.html.haml | 25 +++++++------ config/locales/en.yml | 3 ++ spec/factories.rb | 10 ++++++ spec/features/consumer/account_spec.rb | 17 +++++---- 8 files changed, 64 insertions(+), 51 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/filters/format_balance.js.coffee diff --git a/app/assets/javascripts/darkswarm/filters/format_balance.js.coffee b/app/assets/javascripts/darkswarm/filters/format_balance.js.coffee new file mode 100644 index 0000000000..1b42e9e75d --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/format_balance.js.coffee @@ -0,0 +1,7 @@ +Darkswarm.filter "formatBalance", (localizeCurrencyFilter, tFilter)-> + # Convert number to string currency using injected currency configuration. + (balance) -> + if balance < 0 + tFilter('credit') + ": " + localizeCurrencyFilter(Math.abs(balance)) + else + tFilter('balance_due') + ": " + localizeCurrencyFilter(Math.abs(balance)) diff --git a/app/assets/javascripts/darkswarm/services/orders.js.coffee b/app/assets/javascripts/darkswarm/services/orders.js.coffee index 3cb65278aa..2cc169ebfe 100644 --- a/app/assets/javascripts/darkswarm/services/orders.js.coffee +++ b/app/assets/javascripts/darkswarm/services/orders.js.coffee @@ -4,4 +4,3 @@ Darkswarm.factory 'Orders', (orders_by_distributor, currencyConfig, CurrentHub, # Populate Orders.orders from json in page. @orders_by_distributor = orders_by_distributor @currency_symbol = currencyConfig.symbol - # Sorting by most orders (most recent/frequent?) diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index 45df13a8aa..fc3b9224fe 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -1,5 +1,5 @@ class Api::OrderSerializer < ActiveModel::Serializer - attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :total_money, :balance_money, :payments, :path + attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :payments, :path has_many :payments, serializer: Api::PaymentSerializer @@ -7,8 +7,8 @@ class Api::OrderSerializer < ActiveModel::Serializer object.completed_at.blank? ? "" : object.completed_at.to_formatted_s(:long_ordinal) end - def total_money - to_money(object.total) + def total + object.total.to_money.to_s end def shipment_state @@ -23,17 +23,7 @@ class Api::OrderSerializer < ActiveModel::Serializer object.state ? object.state.humanize : nil # Or a call to t() here? end - def balance_money - to_money(object.outstanding_balance) - end - def path spree.order_url(object.number, only_path: true) end - - private - - def to_money(amount) - {currency_symbol:amount.to_money.currency_symbol, amount:amount.to_money.to_s} - end end diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index b50a5e4f94..3716210d2a 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -2,25 +2,25 @@ .columns.small-12.medium-12.large-12.fat %table %tr - %th{"bo-text" => "'transaction' | t"} - %th{"bo-text" => "'transaction_date' | t"} - %th{"bo-text" => "'payment_state' | t"} - %th{"bo-text" => "'shipping_state' | t"} - %th{"bo-text" => "'value' | t"} - %th{"bo-text" => "'balance' | t"} + %th.order1{"bo-text" => "'transaction' | t"} + %th.order2{"bo-text" => "'transaction_date' | t"} + %th.order3{"bo-text" => "'payment_state' | t"} + %th.order4{"bo-text" => "'shipping_state' | t"} + %th.order5{"bo-text" => "'value' | t"} + %th.order6{"bo-text" => "'balance' | t"} %tbody{"ng-repeat" => "order in distributor.distributed_orders"} %tr - %td + %td.order1 %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} - %td{"bo-text" => "order.completed_at"} - %td{"bo-text" => "order.payment_state | t"} - %td{"bo-text" => "order.shipment_state | t"} - %td.text-right{"bo-text" => "order.total_money.currency_symbol + order.total_money.amount"} - %td.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.balance_money.currency_symbol + order.balance_money.amount"} + %td.order2{"bo-text" => "order.completed_at"} + %td.order3{"bo-text" => "order.payment_state | t"} + %td.order4{"bo-text" => "order.shipment_state | t"} + %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} + %td.order6.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} %tr{"ng-repeat" => "payment in order.payments"} - %td{"bo-text" => "'Payment '+ payment.identifier"} - %td{"bo-text" => "payment.updated_at"} - %td{"bo-text" => "payment.payment_method"} - %td - %td.text-right {{order.total_money.currency_symbol}} {{payment.amount}} - %td + %td.order1{"bo-text" => "'Payment '+ payment.identifier"} + %td.order2{"bo-text" => "payment.updated_at"} + %td.order3{"bo-text" => "payment.payment_method"} + %td.order4 + %td.order5.text-right{"bo-text" => "payment.amount | localizeCurrency"} + %td.order6 diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index fcceb9b02e..2b3f160416 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -1,14 +1,13 @@ -.row.active_table_row{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} - .skinny-head +.row.active_table_row.skinny-head.margin-top{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} + .columns.small-2 %span.margin-top - .columns.medium-2.large-2 - %span.margin-top - %img{"bo-src" => "distributor.logo"} - .columns.small-10.medium-8.large-5 - %span.margin-top - %strong{"bo-text" => "distributor.name"} - .columns.small-6.medium-3.large-3 - %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} - .columns.small-2.medium-2.large-2.text-right - %span.margin-top - %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} + %img{"bo-src" => "distributor.logo"} + .columns.small-10.medium-5 + %span.margin-top + %strong{"bo-text" => "distributor.name"} + .columns.small-8.small-offset-2.medium-3 + %span.margin-top{"bo-text" => "distributor.balance | formatBalance", "ng-class" => "{'credit' : distributor.balance < 0, 'debit' : distributor.balance > 0, 'paid' : distributor.balance == 0}" } + -# %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} + .columns.small-2.medium-2.text-right + %span.margin-top + %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} diff --git a/config/locales/en.yml b/config/locales/en.yml index a6ad408997..e4b780fbbb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -679,5 +679,8 @@ Please follow the instructions there to make your enterprise visible on the Open shipping_state: "Shipping status" value: "Value" "Balance due": "Balance due" # TODO: In /accounts better to pass the symbol and translate rather than humanize + balance_due: "Balance due" + credit: "Credit" Paid: "Paid" Ready: "Ready" + you_have_no_orders_yet: "You have no orders yet" diff --git a/spec/factories.rb b/spec/factories.rb index 8ae6a094c2..94e79fd625 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -189,6 +189,16 @@ FactoryGirl.define do distributor { create(:distributor_enterprise) } end + factory :order_with_credit_payment, parent: :completed_order_with_totals do + distributor { create(:distributor_enterprise)} + order_cycle { create(:simple_order_cycle) } + + after(:create) do |order| + create(:payment, amount: order.total + 10000, order: order, state: "completed") + order.reload + end + end + factory :zone_with_member, :parent => :zone do default_tax true diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb index d81da58acc..7eab4e031c 100644 --- a/spec/features/consumer/account_spec.rb +++ b/spec/features/consumer/account_spec.rb @@ -11,22 +11,27 @@ feature %q{ let!(:user2) {create(:user)} let!(:distributor1) { create(:distributor_enterprise) } let!(:distributor2) { create(:distributor_enterprise) } + let!(:distributor_credit) { create(:distributor_enterprise) } let!(:distributor_without_orders) { create(:distributor_enterprise) } - let!(:d1o1) { create(:completed_order_with_totals, distributor: distributor1, user_id: user.id, total: 10000)} - let!(:d1o2) { create(:completed_order_with_totals, distributor: distributor1, user_id: user.id, total: 5000)} - let!(:d2o1) { create(:completed_order_with_totals, distributor: distributor2, user_id: user.id)} + let!(:d1o1) { create(:completed_order_with_totals, distributor_id: distributor1.id, user_id: user.id, total: 10000)} + let!(:d1o2) { create(:completed_order_with_totals, distributor_id: distributor1.id, user_id: user.id, total: 5000)} + let!(:d2o1) { create(:completed_order_with_totals, distributor_id: distributor2.id, user_id: user.id)} + let!(:credit_order) { create(:order_with_credit_payment, distributor_id: distributor_credit.id, user_id: user.id)} +# let!(:credit_payment) { create(:payment, amount: 12000.00, order_id: credit_order.id)} - let!(:d1o1p) { create(:payment, order: d1o1)} before do + credit_order.update! login_as user visit "/account" end - it "shows all hubs that have been ordered from" do + it "shows all hubs that have been ordered from with balance or credit" do expect(page).to have_content distributor1.name expect(page).to have_content distributor2.name expect(page).not_to have_content distributor_without_orders.name + expect(page).to have_content distributor1.name + " " + "Balance due" + expect(page).to have_content distributor_credit.name + " Credit" end it "reveals table of orders for distributors when clicked" do @@ -43,7 +48,7 @@ feature %q{ end it "displays an appropriate message" do - expect(page).to have_content "You have no orders yet" + expect(page).to have_content {t :you_have_no_orders_yet} end end From 5b73b80e81af4f2933e688b4f25444c85fb60eed Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Thu, 28 Jan 2016 23:10:27 +0000 Subject: [PATCH 033/215] sorting orders in reverse date order --- app/models/spree/user_decorator.rb | 2 +- app/views/spree/users/_fat.html.haml | 6 +++--- app/views/spree/users/show.html.haml | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 7a808e6547..2578f417cc 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -56,7 +56,7 @@ Spree.user_class.class_eval do # Returns orders and their associated payments for all distributors that have been ordered from def orders_by_distributor - Enterprise.includes(distributed_orders: :payments).where(enterprises: {id: self.enterprises_ordered_from }, spree_orders: {state: :complete, user_id: self.id}) + Enterprise.includes(distributed_orders: :payments).where(enterprises: {id: self.enterprises_ordered_from }, spree_orders: {state: 'complete', user_id: self.id}).order('spree_orders.completed_at DESC') end private diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index 3716210d2a..ce6f956bb7 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -8,8 +8,8 @@ %th.order4{"bo-text" => "'shipping_state' | t"} %th.order5{"bo-text" => "'value' | t"} %th.order6{"bo-text" => "'balance' | t"} - %tbody{"ng-repeat" => "order in distributor.distributed_orders"} - %tr + %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders"} + %tr.order-row %td.order1 %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} %td.order2{"bo-text" => "order.completed_at"} @@ -17,7 +17,7 @@ %td.order4{"bo-text" => "order.shipment_state | t"} %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} %td.order6.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} - %tr{"ng-repeat" => "payment in order.payments"} + %tr.payment-row{"ng-repeat" => "payment in order.payments"} %td.order1{"bo-text" => "'Payment '+ payment.identifier"} %td.order2{"bo-text" => "payment.updated_at"} %td.order3{"bo-text" => "payment.payment_method"} diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index 290d1ed36a..482dac507a 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -23,3 +23,4 @@ = render partial: "spree/users/skinny" = render partial: "spree/users/fat" .message{"ng-if" => "Orders.orders_by_distributor.length == 0", "bo-text" => "'you_have_no_orders_yet' | t"} +-# TODO: Add footer From 92d830b8840e2f6ffc3249f2f7b81f156b62f394 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 30 Jan 2016 17:56:04 +0000 Subject: [PATCH 034/215] Table styling --- .../stylesheets/darkswarm/account.css.sass | 36 +++++++++++++++++++ .../stylesheets/darkswarm/producers.css.sass | 2 +- app/views/spree/users/_fat.html.haml | 4 +-- 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 app/assets/stylesheets/darkswarm/account.css.sass diff --git a/app/assets/stylesheets/darkswarm/account.css.sass b/app/assets/stylesheets/darkswarm/account.css.sass new file mode 100644 index 0000000000..285c634d36 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/account.css.sass @@ -0,0 +1,36 @@ +@import branding +@import mixins + +.orders + @include sidepaddingSm + @include panepadding + a + color: $clr-brick + &:hover, &:active, &:focus + color: $clr-brick-med-bright + a.button.primary + &:hover, &:active, &:focus + color: white + .strong + color: $clr-brick + + table + tr:nth-of-type(even) + background: transparent // clear previous + tbody.odd + tr + background-color: #f9f9f9 + border: none + // Column widths for order table + .order1 + width: 25% + .order2 + width: 20% + .order3 + width: 25% + .order4 + width: 10% + .order5 + width: 10% + .order6 + width: 10% diff --git a/app/assets/stylesheets/darkswarm/producers.css.sass b/app/assets/stylesheets/darkswarm/producers.css.sass index 2055a45035..dce972629b 100644 --- a/app/assets/stylesheets/darkswarm/producers.css.sass +++ b/app/assets/stylesheets/darkswarm/producers.css.sass @@ -15,4 +15,4 @@ color: $clr-turquoise-bright a.button.primary &:hover, &:active, &:focus - color: white \ No newline at end of file + color: white diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index ce6f956bb7..24f196f35e 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -1,5 +1,5 @@ .row.active_table_row{"ng-if" => "open()","ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} - .columns.small-12.medium-12.large-12.fat + .columns.small-12.large-10.large-offset-2.fat %table %tr %th.order1{"bo-text" => "'transaction' | t"} @@ -8,7 +8,7 @@ %th.order4{"bo-text" => "'shipping_state' | t"} %th.order5{"bo-text" => "'value' | t"} %th.order6{"bo-text" => "'balance' | t"} - %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders"} + %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders", "ng-class-odd"=>"'odd'", "ng-class-even"=>"'even'"} %tr.order-row %td.order1 %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} From ec6b0f873e09e00656c915fd11a21f5618c1d875 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 30 Jan 2016 19:53:37 +0000 Subject: [PATCH 035/215] style credit/debit, spec updates --- app/assets/stylesheets/darkswarm/account.css.sass | 15 +++++++++++++++ app/views/spree/users/_fat.html.haml | 4 ++-- app/views/spree/users/_skinny.html.haml | 2 +- app/views/spree/users/show.html.haml | 12 +++++------- spec/factories.rb | 10 ++++++++++ spec/features/consumer/account_spec.rb | 2 +- spec/serializers/order_serializer_spec.rb | 13 ------------- 7 files changed, 34 insertions(+), 24 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/account.css.sass b/app/assets/stylesheets/darkswarm/account.css.sass index 285c634d36..fcda6e49f5 100644 --- a/app/assets/stylesheets/darkswarm/account.css.sass +++ b/app/assets/stylesheets/darkswarm/account.css.sass @@ -4,6 +4,11 @@ .orders @include sidepaddingSm @include panepadding + padding-top: 10px + + h3 + padding-top: 2em + a color: $clr-brick &:hover, &:active, &:focus @@ -14,7 +19,17 @@ .strong color: $clr-brick + .credit + color: green + + .debit + color: $clr-brick + + .distributor-balance.paid + visibility: hidden + table + border-radius: 0.5em 0.5em 0 0 tr:nth-of-type(even) background: transparent // clear previous tbody.odd diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index 24f196f35e..9f3462a6d5 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -6,8 +6,8 @@ %th.order2{"bo-text" => "'transaction_date' | t"} %th.order3{"bo-text" => "'payment_state' | t"} %th.order4{"bo-text" => "'shipping_state' | t"} - %th.order5{"bo-text" => "'value' | t"} - %th.order6{"bo-text" => "'balance' | t"} + %th.order5.text-right{"bo-text" => "'value' | t"} + %th.order6.text-right{"bo-text" => "'balance' | t"} %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders", "ng-class-odd"=>"'odd'", "ng-class-even"=>"'even'"} %tr.order-row %td.order1 diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index 2b3f160416..31ebf3ada7 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -6,7 +6,7 @@ %span.margin-top %strong{"bo-text" => "distributor.name"} .columns.small-8.small-offset-2.medium-3 - %span.margin-top{"bo-text" => "distributor.balance | formatBalance", "ng-class" => "{'credit' : distributor.balance < 0, 'debit' : distributor.balance > 0, 'paid' : distributor.balance == 0}" } + %span.margin-top.distributor-balance{"bo-text" => "distributor.balance | formatBalance", "ng-class" => "{'credit' : distributor.balance < 0, 'debit' : distributor.balance > 0, 'paid' : distributor.balance == 0}" } -# %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} .columns.small-2.medium-2.text-right %span.margin-top diff --git a/app/views/spree/users/show.html.haml b/app/views/spree/users/show.html.haml index 482dac507a..83171daf0d 100644 --- a/app/views/spree/users/show.html.haml +++ b/app/views/spree/users/show.html.haml @@ -3,13 +3,10 @@ .row.pad-top .small-12.columns.pad-top - %h1= accurate_title + %h2= accurate_title .account-summary{"data-hook" => "account_summary"} - %dl#user-info - %dt= t(:email) - %dd - = @user.email - (#{link_to t(:edit), spree.edit_account_path}) + = @user.email + (#{link_to t(:edit), spree.edit_account_path}) %h3= t(:my_orders) .orders{"ng-controller" => "OrdersCtrl", "ng-cloak" => true} .row{bindonce: true} @@ -23,4 +20,5 @@ = render partial: "spree/users/skinny" = render partial: "spree/users/fat" .message{"ng-if" => "Orders.orders_by_distributor.length == 0", "bo-text" => "'you_have_no_orders_yet' | t"} --# TODO: Add footer + + = render partial: "shared/footer" diff --git a/spec/factories.rb b/spec/factories.rb index 94e79fd625..4487f6a3d1 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -199,6 +199,16 @@ FactoryGirl.define do end end + factory :order_without_full_payment, parent: :completed_order_with_totals do + distributor { create(:distributor_enterprise)} + order_cycle { create(:simple_order_cycle) } + + after(:create) do |order| + create(:payment, amount: order.total - 1, order: order, state: "completed") + order.reload + end + end + factory :zone_with_member, :parent => :zone do default_tax true diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb index 7eab4e031c..0d84db501e 100644 --- a/spec/features/consumer/account_spec.rb +++ b/spec/features/consumer/account_spec.rb @@ -14,7 +14,7 @@ feature %q{ let!(:distributor_credit) { create(:distributor_enterprise) } let!(:distributor_without_orders) { create(:distributor_enterprise) } let!(:d1o1) { create(:completed_order_with_totals, distributor_id: distributor1.id, user_id: user.id, total: 10000)} - let!(:d1o2) { create(:completed_order_with_totals, distributor_id: distributor1.id, user_id: user.id, total: 5000)} + let!(:d1o2) { create(:order_without_full_payment, distributor_id: distributor1.id, user_id: user.id, total: 5000)} let!(:d2o1) { create(:completed_order_with_totals, distributor_id: distributor2.id, user_id: user.id)} let!(:credit_order) { create(:order_with_credit_payment, distributor_id: distributor_credit.id, user_id: user.id)} # let!(:credit_payment) { create(:payment, amount: 12000.00, order_id: credit_order.id)} diff --git a/spec/serializers/order_serializer_spec.rb b/spec/serializers/order_serializer_spec.rb index f58565de7a..1b7c99715b 100644 --- a/spec/serializers/order_serializer_spec.rb +++ b/spec/serializers/order_serializer_spec.rb @@ -9,19 +9,6 @@ describe Api::OrderSerializer do expect(serializer.to_json).to match order.number.to_s end - it "converts the total to currency and amount" do - expect(serializer.serializable_hash[:total_money].keys).to include :currency_symbol - # Not sure what currency symbol is in test env - expect(serializer.serializable_hash[:total_money].keys).to include :amount - expect(serializer.serializable_hash[:total_money][:amount]).to eq "0.00" - end - - it "converts the balance to currency and amount" do - expect(serializer.serializable_hash[:balance_money].keys).to include :currency_symbol - # Not sure what currency symbol is in test env - expect(serializer.serializable_hash[:balance_money].keys).to include :amount - expect(serializer.serializable_hash[:balance_money][:amount]).to eq "0.00" - end it "convert the state attributes to readable strings" do expect(serializer.to_json).to match "Complete" expect(serializer.to_json).to match "Balance due" From e3b9ce4d80e5ebea53de578b507b3317a3c37e61 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 31 Jan 2016 10:28:19 +0000 Subject: [PATCH 036/215] Delegate url helper to pass specs --- app/serializers/api/order_serializer.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index fc3b9224fe..89b9d3dfa9 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -23,7 +23,8 @@ class Api::OrderSerializer < ActiveModel::Serializer object.state ? object.state.humanize : nil # Or a call to t() here? end + delegate :order_url, to: Spree::Core::Engine.routes_url_helpers def path - spree.order_url(object.number, only_path: true) + order_url(object.number, only_path: true) end end From dcb07237d8d9c55c960d4c1272ef0a3dc4b3e560 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 31 Jan 2016 11:00:07 +0000 Subject: [PATCH 037/215] Spec worked, links didn't :) --- app/serializers/api/order_serializer.rb | 3 +-- spec/features/consumer/account_spec.rb | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index 89b9d3dfa9..162c905e2b 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -23,8 +23,7 @@ class Api::OrderSerializer < ActiveModel::Serializer object.state ? object.state.humanize : nil # Or a call to t() here? end - delegate :order_url, to: Spree::Core::Engine.routes_url_helpers def path - order_url(object.number, only_path: true) + Spree::Core::Engine.routes_url_helpers.order_url(object.number, only_path: true) end end diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb index 0d84db501e..b072a24357 100644 --- a/spec/features/consumer/account_spec.rb +++ b/spec/features/consumer/account_spec.rb @@ -36,7 +36,8 @@ feature %q{ it "reveals table of orders for distributors when clicked" do expand_active_table_node distributor1.name - expect(page).to have_content "Order " + d1o1.number.to_s + expect(page).to have_link "Order " + d1o1.number, href:"/orders/#{d1o1.number}" + expand_active_table_node distributor2.name expect(page).not_to have_content "Order " + d1o1.number.to_s end From 0ad3d8f3512b93f7a785ba5daa4b81f09b4d8192 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 2 Feb 2016 23:21:04 +0000 Subject: [PATCH 038/215] WIP: styling updates --- app/assets/stylesheets/darkswarm/account.css.sass | 11 ++++++----- app/views/spree/users/_fat.html.haml | 2 +- app/views/spree/users/_skinny.html.haml | 2 +- config/locales/en-GB.yml | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/account.css.sass b/app/assets/stylesheets/darkswarm/account.css.sass index fcda6e49f5..29f9e33095 100644 --- a/app/assets/stylesheets/darkswarm/account.css.sass +++ b/app/assets/stylesheets/darkswarm/account.css.sass @@ -13,11 +13,12 @@ color: $clr-brick &:hover, &:active, &:focus color: $clr-brick-med-bright - a.button.primary - &:hover, &:active, &:focus - color: white - .strong - color: $clr-brick + + .account-logo + img + display: block + width: 60px + height: 60px .credit color: green diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index 9f3462a6d5..769810d546 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -18,7 +18,7 @@ %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} %td.order6.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} %tr.payment-row{"ng-repeat" => "payment in order.payments"} - %td.order1{"bo-text" => "'Payment '+ payment.identifier"} + %td.order1{"bo-text" => "()'payment' | t) + ' ' + payment.identifier"} %td.order2{"bo-text" => "payment.updated_at"} %td.order3{"bo-text" => "payment.payment_method"} %td.order4 diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index 31ebf3ada7..5310f06a63 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -1,7 +1,7 @@ .row.active_table_row.skinny-head.margin-top{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} .columns.small-2 %span.margin-top - %img{"bo-src" => "distributor.logo"} + %img.account-logo{"bo-src" => "distributor.logo"} .columns.small-10.medium-5 %span.margin-top %strong{"bo-text" => "distributor.name"} diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index f89a98862d..01475462e0 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -635,4 +635,4 @@ Please follow the instructions there to make your enterprise visible on the Open price_graph: "Price graph" included_tax: "Included tax" remove_tax: "Remove tax" - balance: "Balanceroos" + balance: "Balance" From 83d248469fea0a8cf2cb21481b373d1f6e77cab3 Mon Sep 17 00:00:00 2001 From: stveep Date: Tue, 16 Feb 2016 20:32:09 +0000 Subject: [PATCH 039/215] Running balances --- .../javascripts/darkswarm/services/orders.js.coffee | 8 ++++++++ app/views/spree/users/_fat.html.haml | 3 +++ config/locales/en.yml | 1 + 3 files changed, 12 insertions(+) diff --git a/app/assets/javascripts/darkswarm/services/orders.js.coffee b/app/assets/javascripts/darkswarm/services/orders.js.coffee index 2cc169ebfe..baa46a3d96 100644 --- a/app/assets/javascripts/darkswarm/services/orders.js.coffee +++ b/app/assets/javascripts/darkswarm/services/orders.js.coffee @@ -4,3 +4,11 @@ Darkswarm.factory 'Orders', (orders_by_distributor, currencyConfig, CurrentHub, # Populate Orders.orders from json in page. @orders_by_distributor = orders_by_distributor @currency_symbol = currencyConfig.symbol + + for distributor in @orders_by_distributor + console.log distributor + for order, i in distributor.distributed_orders + balances = distributor.distributed_orders.slice(i,distributor.distributed_orders.length).map (o) -> parseFloat(o.outstanding_balance) + running_balance = balances.reduce (a,b) -> a+b + order.running_balance = running_balance.toFixed(2) + console.log order diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index 769810d546..b4000ca594 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -8,6 +8,7 @@ %th.order4{"bo-text" => "'shipping_state' | t"} %th.order5.text-right{"bo-text" => "'value' | t"} %th.order6.text-right{"bo-text" => "'balance' | t"} + %th.order6.text-right{"bo-text" => "'running_balance' | t"} %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders", "ng-class-odd"=>"'odd'", "ng-class-even"=>"'even'"} %tr.order-row %td.order1 @@ -17,6 +18,7 @@ %td.order4{"bo-text" => "order.shipment_state | t"} %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} %td.order6.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} + %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} %tr.payment-row{"ng-repeat" => "payment in order.payments"} %td.order1{"bo-text" => "()'payment' | t) + ' ' + payment.identifier"} %td.order2{"bo-text" => "payment.updated_at"} @@ -24,3 +26,4 @@ %td.order4 %td.order5.text-right{"bo-text" => "payment.amount | localizeCurrency"} %td.order6 + %td.order7 diff --git a/config/locales/en.yml b/config/locales/en.yml index e673d6bfd9..a1675f951b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -687,3 +687,4 @@ Please follow the instructions there to make your enterprise visible on the Open Paid: "Paid" Ready: "Ready" you_have_no_orders_yet: "You have no orders yet" + running_balance: "Running balance" From 23e774bede4a9081e9d30402ddd9fea72f07e92c Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 20 Feb 2016 17:20:43 +0000 Subject: [PATCH 040/215] Styling, fewer columns in small/medium screen, logo resize --- .../controllers/orders_controller.js.coffee | 10 ++-- .../darkswarm/services/orders.js.coffee | 14 +++-- .../stylesheets/darkswarm/account.css.sass | 18 +++--- app/views/spree/users/_fat.html.haml | 57 ++++++++++--------- app/views/spree/users/_skinny.html.haml | 2 +- config/locales/en-GB.yml | 1 - config/locales/en.yml | 2 +- 7 files changed, 55 insertions(+), 49 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee index f30cfa8436..3b45a2905f 100644 --- a/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee @@ -1,8 +1,8 @@ Darkswarm.controller "OrdersCtrl", ($scope, $rootScope, $timeout, Orders, Search, $document, HashNavigation, FilterSelectorsService, EnterpriseModal, enterpriseMatchesNameQueryFilter, distanceWithinKmFilter) -> $scope.Orders = Orders - $scope.filterEnterprises = -> - es = Enterprises.hubs - $scope.nameMatches = enterpriseMatchesNameQueryFilter(es, true) - $scope.distanceMatches = enterpriseMatchesNameQueryFilter(es, false) - $scope.distanceMatches = distanceWithinKmFilter($scope.distanceMatches, 50) + # $scope.filterEnterprises = -> + # es = Enterprises.hubs + # $scope.nameMatches = enterpriseMatchesNameQueryFilter(es, true) + # $scope.distanceMatches = enterpriseMatchesNameQueryFilter(es, false) + # $scope.distanceMatches = distanceWithinKmFilter($scope.distanceMatches, 50) diff --git a/app/assets/javascripts/darkswarm/services/orders.js.coffee b/app/assets/javascripts/darkswarm/services/orders.js.coffee index baa46a3d96..6c0d74cf2d 100644 --- a/app/assets/javascripts/darkswarm/services/orders.js.coffee +++ b/app/assets/javascripts/darkswarm/services/orders.js.coffee @@ -6,9 +6,11 @@ Darkswarm.factory 'Orders', (orders_by_distributor, currencyConfig, CurrentHub, @currency_symbol = currencyConfig.symbol for distributor in @orders_by_distributor - console.log distributor - for order, i in distributor.distributed_orders - balances = distributor.distributed_orders.slice(i,distributor.distributed_orders.length).map (o) -> parseFloat(o.outstanding_balance) - running_balance = balances.reduce (a,b) -> a+b - order.running_balance = running_balance.toFixed(2) - console.log order + @updateRunningBalance(distributor.distributed_orders) + + + updateRunningBalance: (orders) -> + for order, i in orders + balances = orders.slice(i,orders.length).map (o) -> parseFloat(o.outstanding_balance) + running_balance = balances.reduce (a,b) -> a+b + order.running_balance = running_balance.toFixed(2) diff --git a/app/assets/stylesheets/darkswarm/account.css.sass b/app/assets/stylesheets/darkswarm/account.css.sass index 29f9e33095..9b659c5fd9 100644 --- a/app/assets/stylesheets/darkswarm/account.css.sass +++ b/app/assets/stylesheets/darkswarm/account.css.sass @@ -14,11 +14,10 @@ &:hover, &:active, &:focus color: $clr-brick-med-bright - .account-logo - img - display: block - width: 60px - height: 60px + img + display: block + width: 80px + height: auto .credit color: green @@ -29,6 +28,9 @@ .distributor-balance.paid visibility: hidden + .transaction-group + + table border-radius: 0.5em 0.5em 0 0 tr:nth-of-type(even) @@ -39,14 +41,16 @@ border: none // Column widths for order table .order1 - width: 25% + width: 20% .order2 width: 20% .order3 - width: 25% + width: 20% .order4 width: 10% .order5 width: 10% .order6 width: 10% + .order7 + width: 10% diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index b4000ca594..fd99e325ac 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -1,29 +1,30 @@ .row.active_table_row{"ng-if" => "open()","ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} - .columns.small-12.large-10.large-offset-2.fat - %table - %tr - %th.order1{"bo-text" => "'transaction' | t"} - %th.order2{"bo-text" => "'transaction_date' | t"} - %th.order3{"bo-text" => "'payment_state' | t"} - %th.order4{"bo-text" => "'shipping_state' | t"} - %th.order5.text-right{"bo-text" => "'value' | t"} - %th.order6.text-right{"bo-text" => "'balance' | t"} - %th.order6.text-right{"bo-text" => "'running_balance' | t"} - %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders", "ng-class-odd"=>"'odd'", "ng-class-even"=>"'even'"} - %tr.order-row - %td.order1 - %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} - %td.order2{"bo-text" => "order.completed_at"} - %td.order3{"bo-text" => "order.payment_state | t"} - %td.order4{"bo-text" => "order.shipment_state | t"} - %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} - %td.order6.text-right{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} - %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} - %tr.payment-row{"ng-repeat" => "payment in order.payments"} - %td.order1{"bo-text" => "()'payment' | t) + ' ' + payment.identifier"} - %td.order2{"bo-text" => "payment.updated_at"} - %td.order3{"bo-text" => "payment.payment_method"} - %td.order4 - %td.order5.text-right{"bo-text" => "payment.amount | localizeCurrency"} - %td.order6 - %td.order7 + .columns.small-12.fat + .row + %table + %tr + %th.order1{"bo-text" => "'transaction' | t"} + %th.order2{"bo-text" => "'transaction_date' | t"} + %th.order3.show-for-large-up{"bo-text" => "'payment_state' | t"} + %th.order4.show-for-large-up{"bo-text" => "'shipping_state' | t"} + %th.order5.text-right{"bo-text" => "'value' | t"} + %th.order6.text-right.show-for-large-up{"bo-text" => "'outstanding_balance' | t"} + %th.order7.text-right{"bo-text" => "'running_balance' | t"} + %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders", "ng-class-odd"=>"'odd'", "ng-class-even"=>"'even'"} + %tr.order-row + %td.order1 + %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} + %td.order2{"bo-text" => "order.completed_at"} + %td.order3.show-for-large-up{"bo-text" => "order.payment_state | t"} + %td.order4.show-for-large-up{"bo-text" => "order.shipment_state | t"} + %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} + %td.order6.text-right.show-for-large-up{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} + %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} + %tr.payment-row{"ng-repeat" => "payment in order.payments"} + %td.order1{"bo-text" => "('payment' | t)"} + %td.order2{"bo-text" => "payment.updated_at"} + %td.order3.show-for-large-up{"bo-text" => "payment.payment_method"} + %td.order4.show-for-large-up + %td.order5.text-right{"bo-text" => "payment.amount | localizeCurrency"} + %td.order6.show-for-large-up + %td.order7 diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index 5310f06a63..baec2f4e7a 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -5,7 +5,7 @@ .columns.small-10.medium-5 %span.margin-top %strong{"bo-text" => "distributor.name"} - .columns.small-8.small-offset-2.medium-3 + .columns.small-8.small-offset-2.medium-3.text-right %span.margin-top.distributor-balance{"bo-text" => "distributor.balance | formatBalance", "ng-class" => "{'credit' : distributor.balance < 0, 'debit' : distributor.balance > 0, 'paid' : distributor.balance == 0}" } -# %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} .columns.small-2.medium-2.text-right diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 01475462e0..b2e1d8d96c 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -635,4 +635,3 @@ Please follow the instructions there to make your enterprise visible on the Open price_graph: "Price graph" included_tax: "Included tax" remove_tax: "Remove tax" - balance: "Balance" diff --git a/config/locales/en.yml b/config/locales/en.yml index a1675f951b..5b7f2ed5cd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -681,10 +681,10 @@ Please follow the instructions there to make your enterprise visible on the Open payment_state: "Payment status" shipping_state: "Shipping status" value: "Value" - "Balance due": "Balance due" # TODO: In /accounts better to pass the symbol and translate rather than humanize balance_due: "Balance due" credit: "Credit" Paid: "Paid" Ready: "Ready" you_have_no_orders_yet: "You have no orders yet" running_balance: "Running balance" + outstanding_balance: "Outstanding balance" From d5e51dada6166e332062e28c35f081575e00e440 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 20 Feb 2016 17:22:26 +0000 Subject: [PATCH 041/215] Don't serialize payment ID (not used) --- app/serializers/api/payment_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/serializers/api/payment_serializer.rb b/app/serializers/api/payment_serializer.rb index 029143403f..019e140ff3 100644 --- a/app/serializers/api/payment_serializer.rb +++ b/app/serializers/api/payment_serializer.rb @@ -1,5 +1,5 @@ class Api::PaymentSerializer < ActiveModel::Serializer - attributes :identifier, :amount, :updated_at, :payment_method + attributes :amount, :updated_at, :payment_method def payment_method object.payment_method.name end From d39b62aa31c85731602100492c70690e4c9a5150 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 20 Feb 2016 19:00:37 +0000 Subject: [PATCH 042/215] Fallback to a default tractor logo if missing --- .../darkswarm/controllers/orders_controller.js.coffee | 6 ------ .../darkswarm/directives/logo_fallback.js.coffee | 7 +++++++ app/assets/javascripts/darkswarm/services/orders.js.coffee | 2 +- app/assets/stylesheets/darkswarm/account.css.sass | 7 +++++++ app/views/spree/users/_skinny.html.haml | 2 +- 5 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/directives/logo_fallback.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee index 3b45a2905f..116a2b6fd8 100644 --- a/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/orders_controller.js.coffee @@ -1,8 +1,2 @@ Darkswarm.controller "OrdersCtrl", ($scope, $rootScope, $timeout, Orders, Search, $document, HashNavigation, FilterSelectorsService, EnterpriseModal, enterpriseMatchesNameQueryFilter, distanceWithinKmFilter) -> $scope.Orders = Orders - - # $scope.filterEnterprises = -> - # es = Enterprises.hubs - # $scope.nameMatches = enterpriseMatchesNameQueryFilter(es, true) - # $scope.distanceMatches = enterpriseMatchesNameQueryFilter(es, false) - # $scope.distanceMatches = distanceWithinKmFilter($scope.distanceMatches, 50) diff --git a/app/assets/javascripts/darkswarm/directives/logo_fallback.js.coffee b/app/assets/javascripts/darkswarm/directives/logo_fallback.js.coffee new file mode 100644 index 0000000000..6d54b32008 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/logo_fallback.js.coffee @@ -0,0 +1,7 @@ +Darkswarm.directive "logoFallback", () -> + restrict: "A" + link: (scope, elm, attr)-> + console.log elm + elm.bind('error', -> + elm.replaceWith("") + ) diff --git a/app/assets/javascripts/darkswarm/services/orders.js.coffee b/app/assets/javascripts/darkswarm/services/orders.js.coffee index 6c0d74cf2d..78ba65f79e 100644 --- a/app/assets/javascripts/darkswarm/services/orders.js.coffee +++ b/app/assets/javascripts/darkswarm/services/orders.js.coffee @@ -6,7 +6,7 @@ Darkswarm.factory 'Orders', (orders_by_distributor, currencyConfig, CurrentHub, @currency_symbol = currencyConfig.symbol for distributor in @orders_by_distributor - @updateRunningBalance(distributor.distributed_orders) + @updateRunningBalance(distributor.distributed_orders) updateRunningBalance: (orders) -> diff --git a/app/assets/stylesheets/darkswarm/account.css.sass b/app/assets/stylesheets/darkswarm/account.css.sass index 9b659c5fd9..f582545b74 100644 --- a/app/assets/stylesheets/darkswarm/account.css.sass +++ b/app/assets/stylesheets/darkswarm/account.css.sass @@ -19,6 +19,13 @@ width: 80px height: auto + i.ofn-i_059-producer, i.ofn-i_060-producer-reversed + font-size: 3rem + display: inline-block + margin-right: 0.25rem + float: left + color: $clr-turquoise + .credit color: green diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index baec2f4e7a..aee1884746 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -1,7 +1,7 @@ .row.active_table_row.skinny-head.margin-top{"ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open()}"} .columns.small-2 %span.margin-top - %img.account-logo{"bo-src" => "distributor.logo"} + %img.account-logo{"logo-fallback" => true, "ng-src" => "{{distributor.logo}}"} .columns.small-10.medium-5 %span.margin-top %strong{"bo-text" => "distributor.name"} From 614f83799e7b606c5a381dd428fae358e48deba4 Mon Sep 17 00:00:00 2001 From: Nicolas Blanc Date: Sat, 20 Feb 2016 19:23:22 +0000 Subject: [PATCH 043/215] new test 2016/02/20 --- .../line_items_controller.js.coffee | 6 ++--- app/views/admin/order_cycles/edit.html.haml | 2 +- app/views/admin/order_cycles/index.html.haml | 21 +++++---------- .../admin/orders/bulk_management.html.haml | 2 +- .../single_enterprise_dashboard.html.haml | 26 +++++++++---------- 5 files changed, 25 insertions(+), 32 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 203f7a435a..ce4ef7aa7b 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -70,12 +70,12 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, if $scope.bulk_order_form.$valid StatusMessage.display 'progress', "Saving..." $q.all(LineItems.saveAll()).then(-> - StatusMessage.display 'success', "All changes saved" + StatusMessage.display 'success', "All changes saved"v $scope.bulk_order_form.$setPristine() ).catch -> - StatusMessage.display 'failure', #{t("unsaved_changes_warning")} + StatusMessage.display 'failure', {t("unsaved_changes_warning")} else - StatusMessage.display 'failure', #{t("unsaved_changes_warning")} + StatusMessage.display 'failure', {t("unsaved_changes_warning")} $scope.deleteLineItem = (lineItem) -> if ($scope.confirmDelete && confirm(t("are_you_sure"))) || !$scope.confirmDelete diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 81496bcdb8..fe0892c300 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -5,7 +5,7 @@ %h1 - =t'edit_order_cycle' + = t:edit_order_cycle - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index 91f267a7d7..de166aa4fc 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -28,21 +28,14 @@ %thead %tr - %th - =t'name' - %th - =t'open' - %th - =t'close' + %th =t :name + %th =t :open + %th =t :close - unless order_cycles_simple_index - %th - =t'supplier' - %th - =t'coordinator' - %th - =t'distributors' - %th - =t'products' + %th =t :supplier + %th =t :coordinator + %th =t :distributors + %th =t :products %th.actions %th.actions %th.actions diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 8dda00cf1c..d11f8dffc9 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -151,7 +151,7 @@ = t "products_price" %th.actions %th.actions - = t "ask" + = t "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}}" } diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml index 14bc30e23b..437d28b63c 100644 --- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml @@ -19,7 +19,7 @@ } #package_button %button#toggle_type{ onClick: 'toggleType()' } - = t "change_package" + = t:change_package %i.icon-chevron-down @@ -39,7 +39,7 @@ .alert-box %strong = t "spree_admin_single_enterprise_hint" - %strong= "#{t('manage')} #{@enterprise.name}." + %strong= "#{t:manage} #{@enterprise.name}." %a.close{ href: "#" } × .row @@ -47,15 +47,15 @@ .header %h3 %span.icon-map-marker - = t "your_profil_live" + = t:your_profil_live %p - = t "on_ofn_map" + = t:on_ofn_map .list /-# Can we pass an anchor here to zoom to our enterprise? %a.button.bottom{href: main_app.map_path, target: '_blank'} - = t "see" + = t:see = @enterprise.name - = t "live" + = t:live %span.icon-arrow-right .two.columns   @@ -64,12 +64,12 @@ .header %h3 %span.icon-edit - = t "edit_profile_details" + = t:edit_profile_details %p - = t "edit_profile_details_etc" + = t:edit_profile_details_etc .list %a.button.bottom{href: main_app.edit_admin_enterprise_path(@enterprise)} - = t "manage" + = t:manage = @enterprise.name %span.icon-arrow-right @@ -79,10 +79,10 @@ .header %h3 %span.icon-th-large - = t 'add_and_manage_products' + = t:add_and_manage_products .list %a.button.bottom{href: bulk_edit_admin_products_path} - = t 'manage_products' + = t:manage_products %span.icon-arrow-right .two.columns @@ -93,8 +93,8 @@ .header %h3 %span.icon-shopping-cart - = t 'add_and_manage_order_cycles' + = t:add_and_manage_order_cycles .list %a.button.bottom{href: main_app.admin_order_cycles_path} - = t 'manage_order_cycles' + = t:manage_order_cycles %span.icon-arrow-right From 92cfcb8fc9636b910b773893557bfc18c11f932d Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 20 Feb 2016 21:22:10 +0000 Subject: [PATCH 044/215] remove debug statement --- .../javascripts/darkswarm/directives/logo_fallback.js.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/darkswarm/directives/logo_fallback.js.coffee b/app/assets/javascripts/darkswarm/directives/logo_fallback.js.coffee index 6d54b32008..85f697af3d 100644 --- a/app/assets/javascripts/darkswarm/directives/logo_fallback.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/logo_fallback.js.coffee @@ -1,7 +1,6 @@ Darkswarm.directive "logoFallback", () -> restrict: "A" link: (scope, elm, attr)-> - console.log elm elm.bind('error', -> elm.replaceWith("") ) From f7387048b0d816a5400dc9e66c3ac9aac1a75446 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 21 Feb 2016 12:40:53 +0000 Subject: [PATCH 045/215] Remove cruft and 2nd active table, to stop constant hover --- app/views/spree/users/_fat.html.haml | 57 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index fd99e325ac..e1d1d147b8 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -1,30 +1,29 @@ -.row.active_table_row{"ng-if" => "open()","ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} +.row{"ng-if" => "open()"} .columns.small-12.fat - .row - %table - %tr - %th.order1{"bo-text" => "'transaction' | t"} - %th.order2{"bo-text" => "'transaction_date' | t"} - %th.order3.show-for-large-up{"bo-text" => "'payment_state' | t"} - %th.order4.show-for-large-up{"bo-text" => "'shipping_state' | t"} - %th.order5.text-right{"bo-text" => "'value' | t"} - %th.order6.text-right.show-for-large-up{"bo-text" => "'outstanding_balance' | t"} - %th.order7.text-right{"bo-text" => "'running_balance' | t"} - %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders", "ng-class-odd"=>"'odd'", "ng-class-even"=>"'even'"} - %tr.order-row - %td.order1 - %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} - %td.order2{"bo-text" => "order.completed_at"} - %td.order3.show-for-large-up{"bo-text" => "order.payment_state | t"} - %td.order4.show-for-large-up{"bo-text" => "order.shipment_state | t"} - %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} - %td.order6.text-right.show-for-large-up{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} - %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} - %tr.payment-row{"ng-repeat" => "payment in order.payments"} - %td.order1{"bo-text" => "('payment' | t)"} - %td.order2{"bo-text" => "payment.updated_at"} - %td.order3.show-for-large-up{"bo-text" => "payment.payment_method"} - %td.order4.show-for-large-up - %td.order5.text-right{"bo-text" => "payment.amount | localizeCurrency"} - %td.order6.show-for-large-up - %td.order7 + %table + %tr + %th.order1{"bo-text" => "'transaction' | t"} + %th.order2{"bo-text" => "'transaction_date' | t"} + %th.order3.show-for-large-up{"bo-text" => "'payment_state' | t"} + %th.order4.show-for-large-up{"bo-text" => "'shipping_state' | t"} + %th.order5.text-right{"bo-text" => "'value' | t"} + %th.order6.text-right.show-for-large-up{"bo-text" => "'outstanding_balance' | t"} + %th.order7.text-right{"bo-text" => "'running_balance' | t"} + %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders", "ng-class-odd"=>"'odd'", "ng-class-even"=>"'even'"} + %tr.order-row + %td.order1 + %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} + %td.order2{"bo-text" => "order.completed_at"} + %td.order3.show-for-large-up{"bo-text" => "order.payment_state | t"} + %td.order4.show-for-large-up{"bo-text" => "order.shipment_state | t"} + %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} + %td.order6.text-right.show-for-large-up{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} + %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} + %tr.payment-row{"ng-repeat" => "payment in order.payments"} + %td.order1{"bo-text" => "('payment' | t)"} + %td.order2{"bo-text" => "payment.updated_at"} + %td.order3.show-for-large-up{"bo-text" => "payment.payment_method"} + %td.order4.show-for-large-up + %td.order5.text-right{"bo-text" => "payment.amount | localizeCurrency"} + %td.order6.show-for-large-up + %td.order7 From 1738db9e1f23df0bd725682dda24c93ef8b5a820 Mon Sep 17 00:00:00 2001 From: Nicolas Blanc Date: Mon, 22 Feb 2016 13:00:27 +0000 Subject: [PATCH 046/215] new validation 02/22/2016 --- .../admin/bulk_product_update.js.coffee | 2 +- .../line_items_controller.js.coffee | 2 +- .../single_enterprise_dashboard.html.haml | 26 +++++++++---------- config/locales/en.yml | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 92282ae6ba..cb337c4c47 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -212,7 +212,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout ).error (data, status) -> if status == 400 && data.errors? && data.errors.length > 0 errors = error + "\n" for error in data.errors - alert t("products_update_error") + errors + alert t("products_update_error") + "\n" + errors $scope.displayFailure t("products_update_error") else $scope.displayFailure t("products_update_error_data") + status diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index ce4ef7aa7b..9ab0a71ecc 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -70,7 +70,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, if $scope.bulk_order_form.$valid StatusMessage.display 'progress', "Saving..." $q.all(LineItems.saveAll()).then(-> - StatusMessage.display 'success', "All changes saved"v + StatusMessage.display 'success', "All changes saved" $scope.bulk_order_form.$setPristine() ).catch -> StatusMessage.display 'failure', {t("unsaved_changes_warning")} diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml index 437d28b63c..0d31d482f6 100644 --- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml @@ -31,7 +31,7 @@ .alert-box = t "spree_admin_single_enterprise_alert_mail_confirmation" %strong= "#{@enterprise.name}." - = t(:spree_admin_single_enterprise_alert_mail_sent) + = t "spree_admin_single_enterprise_alert_mail_sent" %strong= "#{@enterprise.email}." = link_to(t('resend'), main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: @enterprise.email } ), method: :post) %a.close{ href: "#" } × @@ -39,7 +39,7 @@ .alert-box %strong = t "spree_admin_single_enterprise_hint" - %strong= "#{t:manage} #{@enterprise.name}." + %strong= "#{t 'manage'} #{@enterprise.name}." %a.close{ href: "#" } × .row @@ -47,15 +47,15 @@ .header %h3 %span.icon-map-marker - = t:your_profil_live + = t "your_profil_live" %p - = t:on_ofn_map + = t "on_ofn_map" .list /-# Can we pass an anchor here to zoom to our enterprise? %a.button.bottom{href: main_app.map_path, target: '_blank'} - = t:see + = t "see" = @enterprise.name - = t:live + = t "live" %span.icon-arrow-right .two.columns   @@ -64,12 +64,12 @@ .header %h3 %span.icon-edit - = t:edit_profile_details + = t "edit_profile_details" %p - = t:edit_profile_details_etc + = t "edit_profile_details_etc" .list %a.button.bottom{href: main_app.edit_admin_enterprise_path(@enterprise)} - = t:manage + = t "manage" = @enterprise.name %span.icon-arrow-right @@ -79,10 +79,10 @@ .header %h3 %span.icon-th-large - = t:add_and_manage_products + = t "add_and_manage_products" .list %a.button.bottom{href: bulk_edit_admin_products_path} - = t:manage_products + = t "manage_products" %span.icon-arrow-right .two.columns @@ -93,8 +93,8 @@ .header %h3 %span.icon-shopping-cart - = t:add_and_manage_order_cycles + = t "add_and_manage_order_cycles" .list %a.button.bottom{href: main_app.admin_order_cycles_path} - = t:manage_order_cycles + = t "manage_order_cycles" %span.icon-arrow-right diff --git a/config/locales/en.yml b/config/locales/en.yml index 5b53145eda..67cfaa2bf3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -31,7 +31,7 @@ en: welcome_to: 'Welcome to ' description: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can…" search_by_name: Search by name or suburb... - producers: Aussie Producers #FIXME + producers: 'Australian Producers' producers_join: Australian producers are now welcome to join the Open Food Network. #FIXME charges_sales_tax: Charges GST? print_invoice: "Print Invoice" From 44508f0f1e01ad5a63fab612ef8db55f9ae2c3e0 Mon Sep 17 00:00:00 2001 From: Nicolas Blanc Date: Mon, 22 Feb 2016 13:38:19 +0000 Subject: [PATCH 047/215] new validation2 02/22/2016 --- .../line_items_controller.js.coffee | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 9ab0a71ecc..4a3a8c84cf 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -5,27 +5,27 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.confirmDelete = true $scope.startDate = formatDate daysFromToday -7 $scope.endDate = formatDate daysFromToday 1 - $scope.bulkActions = [ { name: t("bom_actions_delete"), callback: 'deleteLineItems' } ] + $scope.bulkActions = [ { name: t "bom_actions_delete", callback: 'deleteLineItems' } ] $scope.selectedUnitsProduct = {}; $scope.selectedUnitsVariant = {}; $scope.sharedResource = false $scope.columns = Columns.setColumns - order_no: { name: t("bom_no"), visible: false } - full_name: { name: t("name"), visible: true } - email: { name: t("email"), visible: false } - phone: { name: t("phone"), visible: false } - order_date: { name: t("bom_date"), visible: true } - producer: { name: t("producer"), visible: true } - order_cycle: { name: t("bom_cycle"), visible: false } - hub: { name: t("bom_hub"), visible: false } - variant: { name: t("bom_variant"), visible: true } - quantity: { name: t("bom_quantity"), visible: true } - max: { name: t("bom_max"), visible: true } - final_weight_volume: { name: t("bom_final_weigth_volume"), visible: false } - price: { name: t("bom_price"), visible: false } + order_no: { name: t "bom_no", visible: false } + full_name: { name: t "name", visible: true } + email: { name: t "email", visible: false } + phone: { name: t "phone", visible: false } + order_date: { name: t "bom_date", visible: true } + producer: { name: t "producer", visible: true } + order_cycle: { name: t "bom_cycle", visible: false } + hub: { name: t "bom_hub", visible: false } + variant: { name: t "bom_variant", visible: true } + quantity: { name: t "bom_quantity", visible: true } + max: { name: t "bom_max", visible: true } + final_weight_volume: { name: t "bom_final_weigth_volume", visible: false } + price: { name: t "bom_price", visible: false } $scope.confirmRefresh = -> - LineItems.allSaved() || confirm(t("unsaved_changes_warning")) + LineItems.allSaved() || confirm(t "unsaved_changes_warning") $scope.resetSelectFilters = -> $scope.distributorFilter = blankOption().id @@ -73,12 +73,12 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, StatusMessage.display 'success', "All changes saved" $scope.bulk_order_form.$setPristine() ).catch -> - StatusMessage.display 'failure', {t("unsaved_changes_warning")} + StatusMessage.display 'failure', {t "unsaved_changes_warning"} else - StatusMessage.display 'failure', {t("unsaved_changes_warning")} + StatusMessage.display 'failure', {t "unsaved_changes_warning"} $scope.deleteLineItem = (lineItem) -> - if ($scope.confirmDelete && confirm(t("are_you_sure"))) || !$scope.confirmDelete + if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete LineItems.delete lineItem, => $scope.lineItems.splice $scope.lineItems.indexOf(lineItem), 1 From c91eb9f34515e7d86cf0e64bf310283043f667c9 Mon Sep 17 00:00:00 2001 From: Nicolas Blanc Date: Mon, 22 Feb 2016 14:31:31 +0000 Subject: [PATCH 048/215] new validation3 02/22/2016 --- .../line_items/controllers/line_items_controller.js.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 4a3a8c84cf..d5b71f8c57 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -73,9 +73,9 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, StatusMessage.display 'success', "All changes saved" $scope.bulk_order_form.$setPristine() ).catch -> - StatusMessage.display 'failure', {t "unsaved_changes_warning"} + StatusMessage.display 'failure', t "unsaved_changes_warning" else - StatusMessage.display 'failure', {t "unsaved_changes_warning"} + StatusMessage.display 'failure', t "unsaved_changes_warning" $scope.deleteLineItem = (lineItem) -> if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete From 52d5b8936e70d9587ee62084b26a7b12d61e8400 Mon Sep 17 00:00:00 2001 From: Nicolas Blanc Date: Mon, 22 Feb 2016 14:54:31 +0000 Subject: [PATCH 049/215] new validation4 02/22/2016 --- .../spree/admin/overview/single_enterprise_dashboard.html.haml | 2 +- spec/features/admin/variant_overrides_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml index 0d31d482f6..d2b0635409 100644 --- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml @@ -19,7 +19,7 @@ } #package_button %button#toggle_type{ onClick: 'toggleType()' } - = t:change_package + = t "change_package" %i.icon-chevron-down diff --git a/spec/features/admin/variant_overrides_spec.rb b/spec/features/admin/variant_overrides_spec.rb index 378afa3626..d6dad90b3f 100644 --- a/spec/features/admin/variant_overrides_spec.rb +++ b/spec/features/admin/variant_overrides_spec.rb @@ -54,7 +54,7 @@ feature %q{ end it "displays the list of products with variants" do - page.should have_table_row ['PRODUCER', 'PRODUCT', 'PRICE', 'ON HAND'] + page.should have_table_row ["PRODUCER", "PRODUCT", "PRICE", "ON HAND"] page.should have_table_row [producer.name, product.name, '', ''] page.should have_input "variant-overrides-#{variant.id}-price", placeholder: '1.23' page.should have_input "variant-overrides-#{variant.id}-count_on_hand", placeholder: '12' From 0d5d015d883b6f8096b9e5c653ed278081072814 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 Jan 2016 13:29:28 +1100 Subject: [PATCH 050/215] Extract product JSON rendering to lib class. Fix HTML stripping that never actually worked. --- app/controllers/shop_controller.rb | 72 ++------------ app/serializers/api/product_serializer.rb | 5 + lib/open_food_network/products_renderer.rb | 84 +++++++++++++++++ spec/controllers/shop_controller_spec.rb | 93 +------------------ .../products_renderer_spec.rb | 91 ++++++++++++++++++ 5 files changed, 188 insertions(+), 157 deletions(-) create mode 100644 lib/open_food_network/products_renderer.rb create mode 100644 spec/lib/open_food_network/products_renderer_spec.rb diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index ef78605b41..8d7b9ab274 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -1,4 +1,4 @@ -require 'open_food_network/scope_product_to_hub' +require 'open_food_network/products_renderer' class ShopController < BaseController layout "darkswarm" @@ -10,22 +10,13 @@ class ShopController < BaseController end def products - if @products = products_for_shop + begin + products_json = OpenFoodNetwork::ProductsRenderer.new(current_distributor, current_order_cycle).products - enterprise_fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new current_distributor, current_order_cycle + render json: products_json - render status: 200, - 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, - master_variants: master_variants_for_shop_by_id, - enterprise_fee_calculator: enterprise_fee_calculator, - ).to_json - - else - render json: "", status: 404 + rescue OpenFoodNetwork::ProductsRenderer::NoProducts + render status: 404, json: '' end end @@ -42,55 +33,4 @@ class ShopController < BaseController end end - private - - def products_for_shop - if current_order_cycle - scoper = OpenFoodNetwork::ScopeProductToHub.new(current_distributor) - - current_order_cycle. - valid_products_distributed_by(current_distributor). - order(taxon_order). - each { |p| scoper.scope(p) }. - select { |p| !p.deleted? && p.has_stock_for_distribution?(current_order_cycle, current_distributor) } - end - end - - def taxon_order - if current_distributor.preferred_shopfront_taxon_order.present? - current_distributor - .preferred_shopfront_taxon_order - .split(",").map { |id| "primary_taxon_id=#{id} DESC" } - .join(",") + ", name ASC" - else - "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. - scoper = OpenFoodNetwork::ScopeVariantToHub.new(current_distributor) - Spree::Variant. - for_distribution(current_order_cycle, current_distributor). - each { |v| scoper.scope(v) }. - 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 5a1d1b5c86..03a66afe89 100644 --- a/app/serializers/api/product_serializer.rb +++ b/app/serializers/api/product_serializer.rb @@ -34,6 +34,7 @@ end class Api::CachedProductSerializer < ActiveModel::Serializer #cached #delegate :cache_key, to: :object + include ActionView::Helpers::SanitizeHelper attributes :id, :name, :permalink, :count_on_hand attributes :on_demand, :group_buy, :notes, :description @@ -48,6 +49,10 @@ class Api::CachedProductSerializer < ActiveModel::Serializer has_many :images, serializer: Api::ImageSerializer has_one :supplier, serializer: Api::IdSerializer + def description + strip_tags object.description + end + def properties_with_values object.properties_including_inherited end diff --git a/lib/open_food_network/products_renderer.rb b/lib/open_food_network/products_renderer.rb new file mode 100644 index 0000000000..d745b1b794 --- /dev/null +++ b/lib/open_food_network/products_renderer.rb @@ -0,0 +1,84 @@ +require 'open_food_network/scope_product_to_hub' + +module OpenFoodNetwork + class ProductsRenderer + class NoProducts < Exception; end + + def initialize(distributor, order_cycle) + @distributor = distributor + @order_cycle = order_cycle + end + + def products + products = products_for_shop + + if products + enterprise_fee_calculator = EnterpriseFeeCalculator.new @distributor, @order_cycle + + ActiveModel::ArraySerializer.new(products, + each_serializer: Api::ProductSerializer, + current_order_cycle: @order_cycle, + current_distributor: @distributor, + variants: variants_for_shop_by_id, + master_variants: master_variants_for_shop_by_id, + enterprise_fee_calculator: enterprise_fee_calculator, + ).to_json + else + raise NoProducts.new + end + end + + + private + + def products_for_shop + if @order_cycle + scoper = ScopeProductToHub.new(@distributor) + + @order_cycle. + valid_products_distributed_by(@distributor). + order(taxon_order). + each { |p| scoper.scope(p) }. + select { |p| !p.deleted? && p.has_stock_for_distribution?(@order_cycle, @distributor) } + end + end + + def taxon_order + if @distributor.preferred_shopfront_taxon_order.present? + @distributor + .preferred_shopfront_taxon_order + .split(",").map { |id| "primary_taxon_id=#{id} DESC" } + .join(",") + ", name ASC" + else + "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. + scoper = OpenFoodNetwork::ScopeVariantToHub.new(@distributor) + Spree::Variant. + for_distribution(@order_cycle, @distributor). + each { |v| scoper.scope(v) }. + 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 +end diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index 2c7105b356..09ea85dd44 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -37,7 +37,7 @@ describe ShopController do controller.current_order_cycle.should == oc2 end - context "RABL tests" do + context "JSON tests" do render_views it "should return the order cycle details when the oc is selected" do oc1 = create(:simple_order_cycle, distributors: [d]) @@ -86,7 +86,7 @@ describe ShopController do describe "requests and responses" do let(:product) { create(:product) } before do - exchange.variants << product.master + exchange.variants << product.variants.first end it "returns products via json" do @@ -102,95 +102,6 @@ describe ShopController do response.body.should be_empty end end - - describe "sorting" do - let(:t1) { create(:taxon) } - let(:t2) { create(:taxon) } - let!(:p1) { create(:product, name: "abc", primary_taxon_id: t2.id) } - let!(:p2) { create(:product, name: "def", primary_taxon_id: t1.id) } - let!(:p3) { create(:product, name: "ghi", primary_taxon_id: t2.id) } - let!(:p4) { create(:product, name: "jkl", primary_taxon_id: t1.id) } - - before do - 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 - d.stub(:preferred_shopfront_taxon_order) {"#{t1.id},#{t2.id}"} - controller.stub(:current_order_cycle).and_return order_cycle - xhr :get, :products - assigns[:products].should == [p2, p4, p1, p3] - end - - it "alphabetizes products by name when taxon list is not set" do - d.stub(:preferred_shopfront_taxon_order) {""} - controller.stub(:current_order_cycle).and_return order_cycle - xhr :get, :products - assigns[:products].should == [p1, p2, p3, p4] - end - end - - context "RABL tests" do - render_views - let(:product) { create(:product) } - let(:variant) { product.variants.first } - - before do - 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 - variant.update_attribute(:count_on_hand, 0) - xhr :get, :products - response.body.should_not have_content product.name - end - - it "strips html from description" do - product.update_attribute(:description, "turtles frogs") - xhr :get, :products - response.body.should have_content "frogs" - response.body.should_not have_content " [v1]} end end end diff --git a/spec/lib/open_food_network/products_renderer_spec.rb b/spec/lib/open_food_network/products_renderer_spec.rb new file mode 100644 index 0000000000..875eba1ac9 --- /dev/null +++ b/spec/lib/open_food_network/products_renderer_spec.rb @@ -0,0 +1,91 @@ +require 'spec_helper' +require 'open_food_network/products_renderer' + +module OpenFoodNetwork + describe ProductsRenderer do + let(:d) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [d], coordinator: create(:distributor_enterprise)) } + let(:exchange) { Exchange.find(order_cycle.exchanges.to_enterprises(d).outgoing.first.id) } + let(:pr) { ProductsRenderer.new(d, order_cycle) } + + describe "sorting" do + let(:t1) { create(:taxon) } + let(:t2) { create(:taxon) } + let!(:p1) { create(:product, name: "abc", primary_taxon_id: t2.id) } + let!(:p2) { create(:product, name: "def", primary_taxon_id: t1.id) } + let!(:p3) { create(:product, name: "ghi", primary_taxon_id: t2.id) } + let!(:p4) { create(:product, name: "jkl", primary_taxon_id: t1.id) } + + before do + 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 + d.stub(:preferred_shopfront_taxon_order) {"#{t1.id},#{t2.id}"} + products = pr.send(:products_for_shop) + products.should == [p2, p4, p1, p3] + end + + it "alphabetizes products by name when taxon list is not set" do + d.stub(:preferred_shopfront_taxon_order) {""} + products = pr.send(:products_for_shop) + products.should == [p1, p2, p3, p4] + end + end + + context "JSON tests" do + let(:product) { create(:product) } + let(:variant) { product.variants.first } + + before do + exchange.variants << variant + end + + it "only returns products for the current order cycle" do + pr.products.should include product.name + end + + it "doesn't return products not in stock" do + variant.update_attribute(:count_on_hand, 0) + pr.products.should_not include product.name + end + + it "strips html from description" do + product.update_attribute(:description, "turtles frogs") + json = pr.products + json.should include "frogs" + json.should_not include " [v1]} + end + end + end +end From 920d3bb974d70a19d9b7951c22c31c1ab7615b8c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 Jan 2016 13:29:46 +1100 Subject: [PATCH 051/215] Do not show knapsack time offset warnings unless in CI --- spec/spec_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 80d7c2eeb7..a477ad31e1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,6 +4,7 @@ require 'rubygems' require 'pry' unless ENV['CI'] require 'knapsack' +Knapsack.tracker.config({enable_time_offset_warning: false}) unless ENV['CI'] Knapsack::Adapters::RSpecAdapter.bind ENV["RAILS_ENV"] ||= 'test' From b0207f2b4957779ab29cd6580041ac2fa84a5e2c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 Jan 2016 13:36:12 +1100 Subject: [PATCH 052/215] Clean up specs --- spec/controllers/shop_controller_spec.rb | 46 ++++++++++--------- .../products_renderer_spec.rb | 14 +++--- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index 09ea85dd44..73231bc86c 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe ShopController do - let(:d) { create(:distributor_enterprise) } + let(:distributor) { create(:distributor_enterprise) } it "redirects to the home page if no distributor is selected" do spree_get :show @@ -11,26 +11,26 @@ describe ShopController do describe "with a distributor in place" do before do - controller.stub(:current_distributor).and_return d + controller.stub(:current_distributor).and_return distributor end - describe "Selecting order cycles" do + describe "selecting an order cycle" do it "should select an order cycle when only one order cycle is open" do - oc1 = create(:simple_order_cycle, distributors: [d]) + oc1 = create(:simple_order_cycle, distributors: [distributor]) spree_get :show controller.current_order_cycle.should == oc1 end it "should not set an order cycle when multiple order cycles are open" do - oc1 = create(:simple_order_cycle, distributors: [d]) - oc2 = create(:simple_order_cycle, distributors: [d]) + oc1 = create(:simple_order_cycle, distributors: [distributor]) + oc2 = create(:simple_order_cycle, distributors: [distributor]) spree_get :show - controller.current_order_cycle.should == nil + controller.current_order_cycle.should be_nil end it "should allow the user to post to select the current order cycle" do - oc1 = create(:simple_order_cycle, distributors: [d]) - oc2 = create(:simple_order_cycle, distributors: [d]) + oc1 = create(:simple_order_cycle, distributors: [distributor]) + oc2 = create(:simple_order_cycle, distributors: [distributor]) spree_post :order_cycle, order_cycle_id: oc2.id response.should be_success @@ -39,9 +39,10 @@ describe ShopController do context "JSON tests" do render_views - it "should return the order cycle details when the oc is selected" do - oc1 = create(:simple_order_cycle, distributors: [d]) - oc2 = create(:simple_order_cycle, distributors: [d]) + + it "should return the order cycle details when the OC is selected" do + oc1 = create(:simple_order_cycle, distributors: [distributor]) + oc2 = create(:simple_order_cycle, distributors: [distributor]) spree_post :order_cycle, order_cycle_id: oc2.id response.should be_success @@ -49,7 +50,7 @@ describe ShopController do end it "should return the current order cycle when hit with GET" do - oc1 = create(:simple_order_cycle, distributors: [d]) + oc1 = create(:simple_order_cycle, distributors: [distributor]) controller.stub(:current_order_cycle).and_return oc1 spree_get :order_cycle response.body.should have_content oc1.id @@ -57,13 +58,13 @@ describe ShopController do end it "should not allow the user to select an invalid order cycle" do - oc1 = create(:simple_order_cycle, distributors: [d]) - oc2 = create(:simple_order_cycle, distributors: [d]) + oc1 = create(:simple_order_cycle, distributors: [distributor]) + oc2 = create(:simple_order_cycle, distributors: [distributor]) oc3 = create(:simple_order_cycle, distributors: [create(:distributor_enterprise)]) spree_post :order_cycle, order_cycle_id: oc3.id response.status.should == 404 - controller.current_order_cycle.should == nil + controller.current_order_cycle.should be_nil end end @@ -71,31 +72,32 @@ describe ShopController do describe "producers/suppliers" do let(:supplier) { create(:supplier_enterprise) } let(:product) { create(:product, supplier: supplier) } - let(:order_cycle) { create(:simple_order_cycle, distributors: [d], coordinator: create(:distributor_enterprise)) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) } before do - exchange = Exchange.find(order_cycle.exchanges.to_enterprises(d).outgoing.first.id) + exchange = order_cycle.exchanges.to_enterprises(distributor).outgoing.first exchange.variants << product.master end end describe "returning products" do - let(:order_cycle) { create(:simple_order_cycle, distributors: [d], coordinator: create(:distributor_enterprise)) } - let(:exchange) { Exchange.find(order_cycle.exchanges.to_enterprises(d).outgoing.first.id) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) } + let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first } describe "requests and responses" do let(:product) { create(:product) } + before do exchange.variants << product.variants.first end - it "returns products via json" do + it "returns products via JSON" do controller.stub(:current_order_cycle).and_return order_cycle xhr :get, :products response.should be_success end - it "does not return products if no order_cycle is selected" do + it "does not return products if no order cycle is selected" do controller.stub(:current_order_cycle).and_return nil xhr :get, :products response.status.should == 404 diff --git a/spec/lib/open_food_network/products_renderer_spec.rb b/spec/lib/open_food_network/products_renderer_spec.rb index 875eba1ac9..fac7154dfd 100644 --- a/spec/lib/open_food_network/products_renderer_spec.rb +++ b/spec/lib/open_food_network/products_renderer_spec.rb @@ -3,10 +3,10 @@ require 'open_food_network/products_renderer' module OpenFoodNetwork describe ProductsRenderer do - let(:d) { create(:distributor_enterprise) } - let(:order_cycle) { create(:simple_order_cycle, distributors: [d], coordinator: create(:distributor_enterprise)) } - let(:exchange) { Exchange.find(order_cycle.exchanges.to_enterprises(d).outgoing.first.id) } - let(:pr) { ProductsRenderer.new(d, order_cycle) } + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) } + let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first } + let(:pr) { ProductsRenderer.new(distributor, order_cycle) } describe "sorting" do let(:t1) { create(:taxon) } @@ -24,13 +24,13 @@ module OpenFoodNetwork end it "sorts products by the distributor's preferred taxon list" do - d.stub(:preferred_shopfront_taxon_order) {"#{t1.id},#{t2.id}"} + distributor.stub(:preferred_shopfront_taxon_order) {"#{t1.id},#{t2.id}"} products = pr.send(:products_for_shop) products.should == [p2, p4, p1, p3] end it "alphabetizes products by name when taxon list is not set" do - d.stub(:preferred_shopfront_taxon_order) {""} + distributor.stub(:preferred_shopfront_taxon_order) {""} products = pr.send(:products_for_shop) products.should == [p1, p2, p3, p4] end @@ -81,9 +81,9 @@ module OpenFoodNetwork let(:p) { create(:simple_product) } let!(:v1) { create(:variant, product: p, unit_value: 3) } let!(:v2) { create(:variant, product: p, unit_value: 5) } + let(:pr) { ProductsRenderer.new(hub, oc) } it "scopes variants to distribution" do - pr = ProductsRenderer.new(hub, oc) pr.send(:variants_for_shop_by_id).should == {p.id => [v1]} end end From 6df8f73bb014334cfb2b9d6975b49e08784aa177 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 Jan 2016 13:45:36 +1100 Subject: [PATCH 053/215] Make method naming more explanatory --- app/controllers/shop_controller.rb | 2 +- lib/open_food_network/products_renderer.rb | 6 +++--- .../open_food_network/products_renderer_spec.rb | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index 8d7b9ab274..d1c7e7582e 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -11,7 +11,7 @@ class ShopController < BaseController def products begin - products_json = OpenFoodNetwork::ProductsRenderer.new(current_distributor, current_order_cycle).products + products_json = OpenFoodNetwork::ProductsRenderer.new(current_distributor, current_order_cycle).products_json render json: products_json diff --git a/lib/open_food_network/products_renderer.rb b/lib/open_food_network/products_renderer.rb index d745b1b794..6dd0d5f5ed 100644 --- a/lib/open_food_network/products_renderer.rb +++ b/lib/open_food_network/products_renderer.rb @@ -9,8 +9,8 @@ module OpenFoodNetwork @order_cycle = order_cycle end - def products - products = products_for_shop + def products_json + products = load_products if products enterprise_fee_calculator = EnterpriseFeeCalculator.new @distributor, @order_cycle @@ -31,7 +31,7 @@ module OpenFoodNetwork private - def products_for_shop + def load_products if @order_cycle scoper = ScopeProductToHub.new(@distributor) diff --git a/spec/lib/open_food_network/products_renderer_spec.rb b/spec/lib/open_food_network/products_renderer_spec.rb index fac7154dfd..aa2ef2ec27 100644 --- a/spec/lib/open_food_network/products_renderer_spec.rb +++ b/spec/lib/open_food_network/products_renderer_spec.rb @@ -25,13 +25,13 @@ module OpenFoodNetwork it "sorts products by the distributor's preferred taxon list" do distributor.stub(:preferred_shopfront_taxon_order) {"#{t1.id},#{t2.id}"} - products = pr.send(:products_for_shop) + products = pr.send(:load_products) products.should == [p2, p4, p1, p3] end it "alphabetizes products by name when taxon list is not set" do distributor.stub(:preferred_shopfront_taxon_order) {""} - products = pr.send(:products_for_shop) + products = pr.send(:load_products) products.should == [p1, p2, p3, p4] end end @@ -45,17 +45,17 @@ module OpenFoodNetwork end it "only returns products for the current order cycle" do - pr.products.should include product.name + pr.products_json.should include product.name end it "doesn't return products not in stock" do variant.update_attribute(:count_on_hand, 0) - pr.products.should_not include product.name + pr.products_json.should_not include product.name end it "strips html from description" do product.update_attribute(:description, "turtles frogs") - json = pr.products + json = pr.products_json json.should include "frogs" json.should_not include " Date: Fri, 15 Jan 2016 14:03:58 +1100 Subject: [PATCH 054/215] Add job to cache products JSON --- app/jobs/refresh_products_cache_job.rb | 15 +++++++++++++++ spec/jobs/refresh_products_cache_job_spec.rb | 15 +++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 app/jobs/refresh_products_cache_job.rb create mode 100644 spec/jobs/refresh_products_cache_job_spec.rb diff --git a/app/jobs/refresh_products_cache_job.rb b/app/jobs/refresh_products_cache_job.rb new file mode 100644 index 0000000000..be7fccfb86 --- /dev/null +++ b/app/jobs/refresh_products_cache_job.rb @@ -0,0 +1,15 @@ +require 'open_food_network/products_renderer' + +RefreshProductsCacheJob = Struct.new(:distributor_id, :order_cycle_id) do + def perform + Rails.cache.write "products-json-#{distributor_id}-#{order_cycle_id}", products_json + end + + + private + + def products_json + OpenFoodNetwork::ProductsRenderer.new(distributor_id, order_cycle_id).products_json + end + +end diff --git a/spec/jobs/refresh_products_cache_job_spec.rb b/spec/jobs/refresh_products_cache_job_spec.rb new file mode 100644 index 0000000000..bf70e33e28 --- /dev/null +++ b/spec/jobs/refresh_products_cache_job_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' +require 'open_food_network/products_renderer' + +describe RefreshProductsCacheJob do + let(:distributor_id) { 123 } + let(:order_cycle_id) { 456 } + + it "renders products and writes them to cache" do + OpenFoodNetwork::ProductsRenderer.any_instance.stub(:products_json) { 'products' } + + run_job RefreshProductsCacheJob.new distributor_id, order_cycle_id + + expect(Rails.cache.read("products-json-123-456")).to eq 'products' + end +end From eba636c929f95792a73f9f79f63e124d3ee0e70e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 Jan 2016 12:05:36 +1100 Subject: [PATCH 055/215] When variant is changed/destroyed, trigger product cache refresh --- app/models/spree/variant_decorator.rb | 13 +++++++++++++ spec/models/spree/variant_spec.rb | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 33e7f5bffd..36a4d0e45a 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -1,5 +1,6 @@ require 'open_food_network/enterprise_fee_calculator' require 'open_food_network/variant_and_line_item_naming' +require 'open_food_network/products_cache' Spree::Variant.class_eval do # Remove method From Spree, so method from the naming module is used instead @@ -24,6 +25,18 @@ Spree::Variant.class_eval do before_validation :update_weight_from_unit_value, if: -> v { v.product.present? } after_save :update_units + after_save :refresh_products_cache + around_destroy :refresh_products_cache_from_destroy + + def refresh_products_cache + OpenFoodNetwork::ProductsCache.variant_changed self + end + + def refresh_products_cache_from_destroy + OpenFoodNetwork::ProductsCache.variant_destroyed(self) { yield } + end + + scope :with_order_cycles_inner, joins(exchanges: :order_cycle) diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index cd6bfc1bd3..73789e62c2 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -1,5 +1,6 @@ require 'spec_helper' require 'open_food_network/option_value_namer' +require 'open_food_network/products_cache' module Spree describe Variant do @@ -112,6 +113,21 @@ module Spree end end + describe "callbacks" do + let(:variant) { create(:variant) } + + it "refreshes the products cache on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).with(variant) + variant.sku = 'abc123' + variant.save + end + + it "refreshes the products cache on destroy" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_destroyed).with(variant) + variant.destroy + end + end + describe "indexing variants by id" do let!(:v1) { create(:variant) } let!(:v2) { create(:variant) } From 3621c34bd5240ac89d189d9ddf1e6aff1526d304 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 Jan 2016 13:39:45 +1100 Subject: [PATCH 056/215] Job calls ProductsRenderer with object instances rather than ids --- app/jobs/refresh_products_cache_job.rb | 5 +++-- spec/jobs/refresh_products_cache_job_spec.rb | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/jobs/refresh_products_cache_job.rb b/app/jobs/refresh_products_cache_job.rb index be7fccfb86..0c66925be8 100644 --- a/app/jobs/refresh_products_cache_job.rb +++ b/app/jobs/refresh_products_cache_job.rb @@ -9,7 +9,8 @@ RefreshProductsCacheJob = Struct.new(:distributor_id, :order_cycle_id) do private def products_json - OpenFoodNetwork::ProductsRenderer.new(distributor_id, order_cycle_id).products_json + distributor = Enterprise.find distributor_id + order_cycle = OrderCycle.find order_cycle_id + OpenFoodNetwork::ProductsRenderer.new(distributor, order_cycle).products_json end - end diff --git a/spec/jobs/refresh_products_cache_job_spec.rb b/spec/jobs/refresh_products_cache_job_spec.rb index bf70e33e28..2197ba38c9 100644 --- a/spec/jobs/refresh_products_cache_job_spec.rb +++ b/spec/jobs/refresh_products_cache_job_spec.rb @@ -2,14 +2,24 @@ require 'spec_helper' require 'open_food_network/products_renderer' describe RefreshProductsCacheJob do - let(:distributor_id) { 123 } - let(:order_cycle_id) { 456 } + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle) } it "renders products and writes them to cache" do OpenFoodNetwork::ProductsRenderer.any_instance.stub(:products_json) { 'products' } - run_job RefreshProductsCacheJob.new distributor_id, order_cycle_id + run_job RefreshProductsCacheJob.new distributor.id, order_cycle.id expect(Rails.cache.read("products-json-123-456")).to eq 'products' end + + describe "fetching products JSON" do + let(:job) { RefreshProductsCacheJob.new distributor.id, order_cycle.id } + let(:pr) { double(:products_renderer, products_json: nil) } + + it "fetches products JSON" do + expect(OpenFoodNetwork::ProductsRenderer).to receive(:new).with(distributor, order_cycle) { pr } + job.send(:products_json) + end + end end From 5d20b4fb51934b687b02dfe4730b1a06b9d1ddfe Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 Jan 2016 13:41:30 +1100 Subject: [PATCH 057/215] Add OrderCycle scopes: not_closed, dated --- app/models/order_cycle.rb | 2 ++ spec/factories.rb | 24 ++++++++++++++++++++++-- spec/models/order_cycle_spec.rb | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 03979220d6..b37503151e 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -15,8 +15,10 @@ class OrderCycle < ActiveRecord::Base scope :active_or_complete, lambda { where('order_cycles.orders_open_at <= ?', Time.zone.now) } scope :inactive, lambda { where('order_cycles.orders_open_at > ? OR order_cycles.orders_close_at < ?', Time.zone.now, Time.zone.now) } scope :upcoming, lambda { where('order_cycles.orders_open_at > ?', Time.zone.now) } + scope :not_closed, lambda { where('order_cycles.orders_close_at > ? OR order_cycles.orders_close_at IS NULL', Time.zone.now) } scope :closed, lambda { where('order_cycles.orders_close_at < ?', Time.zone.now).order("order_cycles.orders_close_at DESC") } scope :undated, where('order_cycles.orders_open_at IS NULL OR orders_close_at IS NULL') + scope :dated, where('orders_open_at IS NOT NULL AND orders_close_at IS NOT NULL') scope :soonest_closing, lambda { active.order('order_cycles.orders_close_at ASC') } # TODO This method returns all the closed orders. So maybe we can replace it with :recently_closed. diff --git a/spec/factories.rb b/spec/factories.rb index dd8d02e7bf..6df4216746 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -60,8 +60,8 @@ FactoryGirl.define do factory :simple_order_cycle, :class => OrderCycle do sequence(:name) { |n| "Order Cycle #{n}" } - orders_open_at { Time.zone.now - 1.day } - orders_close_at { Time.zone.now + 1.week } + orders_open_at { 1.day.ago } + orders_close_at { 1.week.from_now } coordinator { Enterprise.is_distributor.first || FactoryGirl.create(:distributor_enterprise) } @@ -84,6 +84,26 @@ FactoryGirl.define do end end + factory :undated_order_cycle, parent: :simple_order_cycle do + orders_open_at nil + orders_close_at nil + end + + factory :upcoming_order_cycle, parent: :simple_order_cycle do + orders_open_at { 1.week.from_now } + orders_close_at { 2.weeks.from_now } + end + + factory :open_order_cycle, parent: :simple_order_cycle do + orders_open_at { 1.week.ago } + orders_close_at { 1.week.from_now } + end + + factory :closed_order_cycle, parent: :simple_order_cycle do + orders_open_at { 2.weeks.ago } + orders_close_at { 1.week.ago } + end + factory :exchange, :class => Exchange do order_cycle { OrderCycle.first || FactoryGirl.create(:simple_order_cycle) } sender { FactoryGirl.create(:enterprise) } diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 90465fb80e..bad2d15312 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -43,6 +43,8 @@ describe OrderCycle do OrderCycle.upcoming.should == [oc_not_yet_open] OrderCycle.closed.should == [oc_already_closed] OrderCycle.undated.should == [oc_undated, oc_undated_open, oc_undated_close] + OrderCycle.not_closed.should == [oc_active, oc_not_yet_open, oc_undated] + OrderCycle.dated.should == [oc_active, oc_not_yet_open, oc_already_closed] end it "finds order cycles accessible by a user" do From a0a61b65cb44355b118e6e98cc5b4c18349d9c6e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 Jan 2016 13:45:28 +1100 Subject: [PATCH 058/215] Refresh the appropriate product caches when a variant is changed --- lib/open_food_network/products_cache.rb | 29 +++++++++ .../open_food_network/products_cache_spec.rb | 65 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 lib/open_food_network/products_cache.rb create mode 100644 spec/lib/open_food_network/products_cache_spec.rb diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb new file mode 100644 index 0000000000..e2d2753cc5 --- /dev/null +++ b/lib/open_food_network/products_cache.rb @@ -0,0 +1,29 @@ +module OpenFoodNetwork + + # When elements of the data model change, enqueue jobs to refresh the appropriate parts of + # the products cache. + class ProductsCache + def self.variant_changed(variant) + exchanges_featuring_variant(variant).each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end + end + + + private + + def self.exchanges_featuring_variant(variant) + Exchange. + outgoing. + with_variant(variant). + joins(:order_cycle). + merge(OrderCycle.dated). + merge(OrderCycle.not_closed) + end + + + def self.refresh_cache(distributor, order_cycle) + Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + end + end +end diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb new file mode 100644 index 0000000000..c00fb16da0 --- /dev/null +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -0,0 +1,65 @@ +require 'open_food_network/products_cache' + +module OpenFoodNetwork + describe ProductsCache do + describe "when a variant changes" do + let(:variant) { create(:variant) } + let(:variant_undistributed) { create(:variant) } + let(:supplier) { create(:supplier_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let(:distributor) { create(:distributor_enterprise) } + let(:oc_undated) { create(:undated_order_cycle, distributors: [distributor], variants: [variant]) } + let(:oc_upcoming) { create(:upcoming_order_cycle, suppliers: [supplier], coordinator: coordinator, distributors: [distributor], variants: [variant]) } + let(:oc_open) { create(:open_order_cycle, distributors: [distributor], variants: [variant]) } + let(:oc_closed) { create(:closed_order_cycle, distributors: [distributor], variants: [variant]) } + + it "refreshes distributions with upcoming order cycles" do + oc_upcoming + expect(ProductsCache).to receive(:refresh_cache).with(distributor, oc_upcoming) + ProductsCache.variant_changed variant + end + + it "refreshes distributions with open order cycles" do + oc_open + expect(ProductsCache).to receive(:refresh_cache).with(distributor, oc_open) + ProductsCache.variant_changed variant + end + + it "does not refresh distributions with undated order cycles" do + oc_undated + expect(ProductsCache).not_to receive(:refresh_cache).with(distributor, oc_undated) + ProductsCache.variant_changed variant + end + + it "does not refresh distributions with closed order cycles" do + oc_closed + expect(ProductsCache).not_to receive(:refresh_cache).with(distributor, oc_closed) + ProductsCache.variant_changed variant + end + + it "limits refresh to outgoing exchanges" do + oc_upcoming + expect(ProductsCache).not_to receive(:refresh_cache).with(coordinator, oc_upcoming) + ProductsCache.variant_changed variant + end + + it "does not refresh distributions where the variant does not appear" do + oc_undated; oc_upcoming; oc_open; oc_closed + variant_undistributed + expect(ProductsCache).not_to receive(:refresh_cache) + ProductsCache.variant_changed variant_undistributed + end + end + + describe "refreshing the cache" do + let(:distributor) { double(:distributor, id: 123) } + let(:order_cycle) { double(:order_cycle, id: 456) } + + it "enqueues a RefreshProductsCacheJob" do + expect do + ProductsCache.send(:refresh_cache, distributor, order_cycle) + end.to enqueue_job RefreshProductsCacheJob, distributor_id: distributor.id, order_cycle_id: order_cycle.id + end + end + end +end From 6d39cc39c6d87bd9be52f0151ec7b75a524dd011 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 Jan 2016 15:17:17 +1100 Subject: [PATCH 059/215] When a variant is destroyed, update product cache --- app/models/spree/variant_decorator.rb | 29 ++++++++++++------- lib/open_food_network/products_cache.rb | 11 +++++++ .../open_food_network/products_cache_spec.rb | 19 ++++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 36a4d0e45a..143508185a 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -10,7 +10,7 @@ Spree::Variant.class_eval do include OpenFoodNetwork::VariantAndLineItemNaming - has_many :exchange_variants, dependent: :destroy + has_many :exchange_variants has_many :exchanges, through: :exchange_variants has_many :variant_overrides @@ -26,16 +26,7 @@ Spree::Variant.class_eval do before_validation :update_weight_from_unit_value, if: -> v { v.product.present? } after_save :update_units after_save :refresh_products_cache - around_destroy :refresh_products_cache_from_destroy - - def refresh_products_cache - OpenFoodNetwork::ProductsCache.variant_changed self - end - - def refresh_products_cache_from_destroy - OpenFoodNetwork::ProductsCache.variant_destroyed(self) { yield } - end - + around_destroy :destruction scope :with_order_cycles_inner, joins(exchanges: :order_cycle) @@ -99,4 +90,20 @@ Spree::Variant.class_eval do def update_weight_from_unit_value self.weight = weight_from_unit_value if self.product.variant_unit == 'weight' && unit_value.present? end + + def refresh_products_cache + OpenFoodNetwork::ProductsCache.variant_changed self + end + + def destruction + OpenFoodNetwork::ProductsCache.variant_destroyed(self) do + # Remove this association here instead of using dependent: :destroy because + # dependent-destroy acts before this around_filter is called, so ProductsCache + # has no way of knowing which exchanges the variant was a member of. + exchange_variants.destroy_all + + # Destroy the variant + yield + end + end end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index e2d2753cc5..b5e83d7590 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -10,6 +10,17 @@ module OpenFoodNetwork end + def self.variant_destroyed(variant, &block) + exchanges = exchanges_featuring_variant(variant).to_a + + block.call + + exchanges.each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end + end + + private def self.exchanges_featuring_variant(variant) diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index c00fb16da0..afc54ae87b 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -51,6 +51,25 @@ module OpenFoodNetwork end end + describe "when a variant is destroyed" do + let(:variant) { create(:variant) } + let(:distributor) { create(:distributor_enterprise) } + let!(:oc) { create(:open_order_cycle, distributors: [distributor], variants: [variant]) } + + it "refreshes the cache based on exchanges the variant was in before destruction" do + expect(ProductsCache).to receive(:refresh_cache).with(distributor, oc) + variant.destroy + end + + it "performs the cache refresh after the variant has been destroyed" do + expect(ProductsCache).to receive(:refresh_cache).with(distributor, oc) do + expect(Spree::Variant.where(id: variant.id)).to be_empty + end + + variant.destroy + end + end + describe "refreshing the cache" do let(:distributor) { double(:distributor, id: 123) } let(:order_cycle) { double(:order_cycle, id: 456) } From 0c0c98a0b06367e97e13ac8c7f1d005f47156881 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 Jan 2016 15:41:17 +1100 Subject: [PATCH 060/215] Refresh products cache on product change --- app/models/spree/product_decorator.rb | 14 +++++++++----- lib/open_food_network/products_cache.rb | 4 ++++ spec/models/spree/product_spec.rb | 13 +++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index b089c9a52b..e969dd500b 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -22,8 +22,6 @@ Spree::Product.class_eval do attr_accessible :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value attr_accessible :inherits_properties, :sku - before_validation :sanitize_permalink - # 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" } @@ -35,11 +33,13 @@ Spree::Product.class_eval do validates_presence_of :variant_unit_name, if: -> p { p.variant_unit == 'items' } - after_save :ensure_standard_variant after_initialize :set_available_on_to_now, :if => :new_record? - after_save :update_units - after_touch :touch_distributors + before_validation :sanitize_permalink before_save :add_primary_taxon_to_taxons + after_touch :touch_distributors + after_save :ensure_standard_variant + after_save :update_units + after_save :refresh_products_cache # -- Joins @@ -251,4 +251,8 @@ Spree::Product.class_eval do self.permalink = create_unique_permalink(requested.parameterize) end end + + def refresh_products_cache + OpenFoodNetwork::ProductsCache.product_changed self + end end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index b5e83d7590..eae1a62d15 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -21,6 +21,10 @@ module OpenFoodNetwork end + def self.product_changed(product) + end + + private def self.exchanges_featuring_variant(variant) diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 25913f679b..75180d8905 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -161,6 +161,19 @@ module Spree end end + describe "callbacks" do + let(:product) { create(:simple_product) } + + it "refreshes the products cache on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + product.name = 'asdf' + product.save + end + + # On destroy, all distributed variants are refreshed by a Variant around_destroy + # callback, so we don't need to do anything on the product model. + end + describe "scopes" do describe "in_supplier" do it "shows products in supplier" do From c98e44c5a1de6cd0b238e1dbcd09e7d2334cd388 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 Jan 2016 16:00:55 +1100 Subject: [PATCH 061/215] Perform refresh of products cache on product change --- lib/open_food_network/products_cache.rb | 11 +++++++---- .../open_food_network/products_cache_spec.rb | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index eae1a62d15..66a2c8b164 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -4,14 +4,14 @@ module OpenFoodNetwork # the products cache. class ProductsCache def self.variant_changed(variant) - exchanges_featuring_variant(variant).each do |exchange| + exchanges_featuring_variants(variant).each do |exchange| refresh_cache exchange.receiver, exchange.order_cycle end end def self.variant_destroyed(variant, &block) - exchanges = exchanges_featuring_variant(variant).to_a + exchanges = exchanges_featuring_variants(variant).to_a block.call @@ -22,15 +22,18 @@ module OpenFoodNetwork def self.product_changed(product) + exchanges_featuring_variants(product.variants).each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end end private - def self.exchanges_featuring_variant(variant) + def self.exchanges_featuring_variants(variants) Exchange. outgoing. - with_variant(variant). + with_any_variant(variants). joins(:order_cycle). merge(OrderCycle.dated). merge(OrderCycle.not_closed) diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index afc54ae87b..d48f5e7d5c 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -70,6 +70,25 @@ module OpenFoodNetwork end end + describe "when a product changes" do + let(:product) { create(:simple_product) } + let(:v1) { create(:variant, product: product) } + let(:v2) { create(:variant, product: product) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let(:oc) { create(:open_order_cycle) } + let!(:ex1) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d1, variants: [v1]) } + let!(:ex2) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d2, variants: [v1, v2]) } + + before { product.reload } + + it "refreshes the distribution each variant appears in, once each" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc).once + ProductsCache.product_changed product + end + end + describe "refreshing the cache" do let(:distributor) { double(:distributor, id: 123) } let(:order_cycle) { double(:order_cycle, id: 456) } From 5f188650d8b66050a55349616d65f16b5a1334de Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 10:01:14 +1100 Subject: [PATCH 062/215] Trigger cache refresh on VariantOverride save/destroy --- app/models/variant_override.rb | 11 +++++++++++ lib/open_food_network/products_cache.rb | 8 ++++++++ spec/models/variant_override_spec.rb | 16 ++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index 21820ce0db..9935055b40 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -6,6 +6,10 @@ class VariantOverride < ActiveRecord::Base # Default stock can be nil, indicating stock should not be reset or zero, meaning reset to zero. Need to ensure this can be set by the user. validates :default_stock, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true + after_save :refresh_products_cache_from_save + after_destroy :refresh_products_cache_from_destroy + + scope :for_hubs, lambda { |hubs| where(hub_id: hubs) } @@ -73,4 +77,11 @@ class VariantOverride < ActiveRecord::Base VariantOverride.where(variant_id: variant, hub_id: hub).first end + def refresh_products_cache_from_save + OpenFoodNetwork::ProductsCache.variant_override_changed self + end + + def refresh_products_cache_from_destroy + OpenFoodNetwork::ProductsCache.variant_override_destroyed self + end end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 66a2c8b164..2666f7ffc7 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -28,6 +28,14 @@ module OpenFoodNetwork end + def self.variant_override_changed(variant_override) + end + + + def self.variant_override_destroyed(variant_override) + end + + private def self.exchanges_featuring_variants(variants) diff --git a/spec/models/variant_override_spec.rb b/spec/models/variant_override_spec.rb index ed2f2c00f4..1c008d05be 100644 --- a/spec/models/variant_override_spec.rb +++ b/spec/models/variant_override_spec.rb @@ -24,6 +24,22 @@ describe VariantOverride do end + describe "callbacks" do + let!(:vo) { create(:variant_override, hub: hub, variant: variant) } + + it "refreshes the products cache on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_override_changed).with(vo) + vo.price = 123.45 + vo.save + end + + it "refreshes the products cache on destroy" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_override_destroyed).with(vo) + vo.destroy + end + end + + describe "looking up prices" do it "returns the numeric price when present" do VariantOverride.create!(variant: variant, hub: hub, price: 12.34) From b7a88fd03b9c9fe7a3b39ff912abb445935a3de3 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 10:14:15 +1100 Subject: [PATCH 063/215] Perform refresh of products cache for variant override change --- lib/open_food_network/products_cache.rb | 11 +++++++++-- .../open_food_network/products_cache_spec.rb | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 2666f7ffc7..556b488316 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -29,6 +29,9 @@ module OpenFoodNetwork def self.variant_override_changed(variant_override) + exchanges_featuring_variants(variant_override.variant, distributor: variant_override.hub).each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end end @@ -38,13 +41,17 @@ module OpenFoodNetwork private - def self.exchanges_featuring_variants(variants) - Exchange. + def self.exchanges_featuring_variants(variants, distributor: nil) + exchanges = Exchange. outgoing. with_any_variant(variants). joins(:order_cycle). merge(OrderCycle.dated). merge(OrderCycle.not_closed) + + exchanges = exchanges.to_enterprise(distributor) if distributor + + exchanges end diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index d48f5e7d5c..ef3f3ed279 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -89,6 +89,25 @@ module OpenFoodNetwork end end + describe "when a variant override changes" do + let(:variant) { create(:variant) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let!(:vo) { create(:variant_override, variant: variant, hub: d1) } + let!(:oc) { create(:open_order_cycle, distributors: [d1, d2], variants: [variant]) } + + it "refreshes the distributions that the variant override affects" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.variant_override_changed vo + end + + it "does not refresh other distributors of the variant" do + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc).never + ProductsCache.variant_override_changed vo + end + end + + describe "refreshing the cache" do let(:distributor) { double(:distributor, id: 123) } let(:order_cycle) { double(:order_cycle, id: 456) } From 1ec329284c15d6004011f22d98fc5725f042a374 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 10:16:41 +1100 Subject: [PATCH 064/215] Refresh products cache when a variant override is destroyed --- lib/open_food_network/products_cache.rb | 1 + spec/lib/open_food_network/products_cache_spec.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 556b488316..1d1dab8cd0 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -36,6 +36,7 @@ module OpenFoodNetwork def self.variant_override_destroyed(variant_override) + variant_override_changed variant_override end diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index ef3f3ed279..2004ececaa 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -108,6 +108,16 @@ module OpenFoodNetwork end + describe "when a variant override is destroyed" do + let(:vo) { double(:variant_override) } + + it "performs the same refresh as a variant override change" do + expect(ProductsCache).to receive(:variant_override_changed).with(vo) + ProductsCache.variant_override_destroyed vo + end + end + + describe "refreshing the cache" do let(:distributor) { double(:distributor, id: 123) } let(:order_cycle) { double(:order_cycle, id: 456) } From fe41430d1ec43021c1f4b6e0d27328ba9ee56407 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 11:41:02 +1100 Subject: [PATCH 065/215] Rerefesh products cache when an order cycle is changed --- app/models/order_cycle.rb | 12 +++++ lib/open_food_network/products_cache.rb | 9 ++++ .../open_food_network/products_cache_spec.rb | 46 +++++++++++++++++++ spec/models/order_cycle_spec.rb | 18 +++++++- 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index b37503151e..d3f11860e9 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -11,6 +11,9 @@ class OrderCycle < ActiveRecord::Base validates_presence_of :name, :coordinator_id + after_save :refresh_products_cache + + scope :active, lambda { where('order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?', Time.zone.now, Time.zone.now) } scope :active_or_complete, lambda { where('order_cycles.orders_open_at <= ?', Time.zone.now) } scope :inactive, lambda { where('order_cycles.orders_open_at > ? OR order_cycles.orders_close_at < ?', Time.zone.now, Time.zone.now) } @@ -183,6 +186,10 @@ class OrderCycle < ActiveRecord::Base self.variants.include? variant end + def dated? + !undated? + end + def undated? self.orders_open_at.nil? || self.orders_close_at.nil? end @@ -245,4 +252,9 @@ class OrderCycle < ActiveRecord::Base distributed_variants.include?(product.master) && (product.variants & distributed_variants).empty? end + + def refresh_products_cache + OpenFoodNetwork::ProductsCache.order_cycle_changed self + end + end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 1d1dab8cd0..8c22c40db7 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -40,6 +40,15 @@ module OpenFoodNetwork end + def self.order_cycle_changed(order_cycle) + if order_cycle.dated? && !order_cycle.closed? + order_cycle.exchanges.outgoing.each do |exchange| + refresh_cache exchange.receiver, order_cycle + end + end + end + + private def self.exchanges_featuring_variants(variants, distributor: nil) diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index 2004ececaa..c44d47fca2 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -118,6 +118,52 @@ module OpenFoodNetwork end + describe "when an order cycle is changed" do + let(:variant) { create(:variant) } + let(:s) { create(:supplier_enterprise) } + let(:c) { create(:distributor_enterprise) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let!(:oc_open) { create(:open_order_cycle, suppliers: [s], coordinator: c, distributors: [d1, d2], variants: [variant]) } + let!(:oc_upcoming) { create(:upcoming_order_cycle, suppliers: [s], coordinator: c, distributors: [d1, d2], variants: [variant]) } + + before do + oc_open.reload + oc_upcoming.reload + end + + it "updates each outgoing distribution in an upcoming order cycle" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc_upcoming).once + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc_upcoming).once + ProductsCache.order_cycle_changed oc_upcoming + end + + it "updates each outgoing distribution in an open order cycle" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc_open).once + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc_open).once + ProductsCache.order_cycle_changed oc_open + end + + it "does nothing when the order cycle has been made undated" do + expect(ProductsCache).to receive(:refresh_cache).never + oc_open.orders_open_at = oc_open.orders_close_at = nil + oc_open.save! + end + + it "does nothing when the order cycle has been closed" do + expect(ProductsCache).to receive(:refresh_cache).never + oc_open.orders_open_at = 2.weeks.ago + oc_open.orders_close_at = 1.week.ago + oc_open.save! + end + + it "does not update incoming exchanges" do + expect(ProductsCache).to receive(:refresh_cache).with(c, oc_open).never + ProductsCache.order_cycle_changed oc_open + end + end + + describe "refreshing the cache" do let(:distributor) { double(:distributor, id: 123) } let(:order_cycle) { double(:order_cycle, id: 456) } diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index bad2d15312..6ef7062f8e 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -20,6 +20,18 @@ describe OrderCycle do oc.save! end + describe "products cache" do + let(:oc) { create(:open_order_cycle) } + + it "refreshes the products cache on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:order_cycle_changed).with(oc) + oc.name = 'asdf' + oc.save + end + + # On destroy, we're removing distributions, so no updates to the products cache are required + end + it "has exchanges" do oc = create(:simple_order_cycle) @@ -330,6 +342,7 @@ describe OrderCycle do it "reports status when an order cycle is upcoming" do Timecop.freeze(oc.orders_open_at - 1.second) do oc.should_not be_undated + oc.should be_dated oc.should be_upcoming oc.should_not be_open oc.should_not be_closed @@ -338,6 +351,7 @@ describe OrderCycle do it "reports status when an order cycle is open" do oc.should_not be_undated + oc.should be_dated oc.should_not be_upcoming oc.should be_open oc.should_not be_closed @@ -346,6 +360,7 @@ describe OrderCycle do it "reports status when an order cycle has closed" do Timecop.freeze(oc.orders_close_at + 1.second) do oc.should_not be_undated + oc.should be_dated oc.should_not be_upcoming oc.should_not be_open oc.should be_closed @@ -355,7 +370,8 @@ describe OrderCycle do it "reports status when an order cycle is undated" do oc.update_attributes!(orders_open_at: nil, orders_close_at: nil) - oc.should be_undated + oc.should be_undated + oc.should_not be_dated oc.should_not be_upcoming oc.should_not be_open oc.should_not be_closed From 378a703cc3e9a572679c844c8e47863bfb20864f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 12:24:04 +1100 Subject: [PATCH 066/215] Order cycles are undated unless they have both open and close dates defined --- spec/models/order_cycle_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 6ef7062f8e..9dc8a0cf91 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -55,7 +55,7 @@ describe OrderCycle do OrderCycle.upcoming.should == [oc_not_yet_open] OrderCycle.closed.should == [oc_already_closed] OrderCycle.undated.should == [oc_undated, oc_undated_open, oc_undated_close] - OrderCycle.not_closed.should == [oc_active, oc_not_yet_open, oc_undated] + OrderCycle.not_closed.should == [oc_active, oc_not_yet_open, oc_undated, oc_undated_open, oc_undated_close] OrderCycle.dated.should == [oc_active, oc_not_yet_open, oc_already_closed] end @@ -381,6 +381,7 @@ describe OrderCycle do oc.update_attributes!(orders_close_at: nil) oc.should be_undated + oc.should_not be_dated oc.should_not be_upcoming oc.should_not be_open oc.should_not be_closed @@ -390,6 +391,7 @@ describe OrderCycle do oc.update_attributes!(orders_open_at: nil) oc.should be_undated + oc.should_not be_dated oc.should_not be_upcoming oc.should_not be_open oc.should_not be_closed From 0c65d1ddd88a93f1bcf168f419bb5dd8a1b3692f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 15:31:14 +1100 Subject: [PATCH 067/215] Trigger products cache refresh when enterprise fee changed or destroyed --- app/models/enterprise_fee.rb | 26 +++++++++++++++++++++++-- lib/open_food_network/products_cache.rb | 9 +++++++++ spec/models/enterprise_fee_spec.rb | 15 ++++++++++++-- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index e19f91d0f4..e2831a4178 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -2,10 +2,11 @@ class EnterpriseFee < ActiveRecord::Base belongs_to :enterprise belongs_to :tax_category, class_name: 'Spree::TaxCategory', foreign_key: 'tax_category_id' has_and_belongs_to_many :order_cycles, join_table: 'coordinator_fees' - has_many :exchange_fees, dependent: :destroy + has_many :exchange_fees has_many :exchanges, through: :exchange_fees - before_destroy { order_cycles.clear } + after_save :refresh_products_cache + around_destroy :destruction calculated_adjustments @@ -57,4 +58,25 @@ class EnterpriseFee < ActiveRecord::Base :mandatory => mandatory, :locked => true}, :without_protection => true) end + + + private + + def refresh_products_cache + OpenFoodNetwork::ProductsCache.enterprise_fee_changed self + end + + def destruction + OpenFoodNetwork::ProductsCache.enterprise_fee_destroyed(self) do + # Remove this association here instead of using dependent: :destroy because + # dependent-destroy acts before this around_filter is called, so ProductsCache + # has no way of knowing where this fee was used. + order_cycles.clear + exchange_fees.destroy_all + + # Destroy the enterprise fee + yield + end + + end end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 8c22c40db7..858316f7f8 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -49,6 +49,15 @@ module OpenFoodNetwork end + def self.enterprise_fee_changed(enterprise_fee) + end + + + def self.enterprise_fee_destroyed(enterprise_fee, &block) + block.call + end + + private def self.exchanges_featuring_variants(variants, distributor: nil) diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb index f0f9df1f0b..7140feda4a 100644 --- a/spec/models/enterprise_fee_spec.rb +++ b/spec/models/enterprise_fee_spec.rb @@ -10,8 +10,20 @@ describe EnterpriseFee do end describe "callbacks" do + let(:ef) { create(:enterprise_fee) } + + it "refreshes the products cache when saved" do + expect(OpenFoodNetwork::ProductsCache).to receive(:enterprise_fee_changed).with(ef) + ef.name = 'foo' + ef.save + end + + it "refreshes the products cache when destroyed" do + expect(OpenFoodNetwork::ProductsCache).to receive(:enterprise_fee_destroyed).with(ef) + ef.destroy + end + it "removes itself from order cycle coordinator fees when destroyed" do - ef = create(:enterprise_fee) oc = create(:simple_order_cycle, coordinator_fees: [ef]) ef.destroy @@ -19,7 +31,6 @@ describe EnterpriseFee do end it "removes itself from order cycle exchange fees when destroyed" do - ef = create(:enterprise_fee) oc = create(:simple_order_cycle) ex = create(:exchange, order_cycle: oc, enterprise_fees: [ef]) From af7e3380d3be4866e64649280a3392525abc5f61 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 15:35:26 +1100 Subject: [PATCH 068/215] Perform refresh of products cache when coordinator fee is changed --- lib/open_food_network/products_cache.rb | 8 ++++++++ .../open_food_network/products_cache_spec.rb | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 858316f7f8..639076fb82 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -50,6 +50,7 @@ module OpenFoodNetwork def self.enterprise_fee_changed(enterprise_fee) + refresh_coordinator_fee enterprise_fee end @@ -74,6 +75,13 @@ module OpenFoodNetwork end + def self.refresh_coordinator_fee(enterprise_fee) + enterprise_fee.order_cycles.each do |order_cycle| + order_cycle_changed order_cycle + end + end + + def self.refresh_cache(distributor, order_cycle) Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id end diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index c44d47fca2..26f3a9b844 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -164,6 +164,23 @@ module OpenFoodNetwork end + describe "when an enterprise fee is changed" do + let(:s) { create(:supplier_enterprise) } + let(:c) { create(:distributor_enterprise) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let(:ef) { create(:enterprise_fee) } + let(:ef_coord) { create(:enterprise_fee, order_cycles: [oc]) } + let(:oc) { create(:open_order_cycle, coordinator: c) } + + + it "updates order cycles when it's a coordinator fee" do + ef_coord + expect(ProductsCache).to receive(:order_cycle_changed).with(oc).once + ProductsCache.enterprise_fee_changed ef_coord + end + end + describe "refreshing the cache" do let(:distributor) { double(:distributor, id: 123) } let(:order_cycle) { double(:order_cycle, id: 456) } From fbedff4eca83bb336b7ffa3f7e370c3eaa0dab80 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 15:42:41 +1100 Subject: [PATCH 069/215] Perform refresh of products cache when distributor fee is changed --- lib/open_food_network/products_cache.rb | 14 ++++++ .../open_food_network/products_cache_spec.rb | 48 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 639076fb82..5013f5b37a 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -51,6 +51,7 @@ module OpenFoodNetwork def self.enterprise_fee_changed(enterprise_fee) refresh_coordinator_fee enterprise_fee + refresh_distributor_fee enterprise_fee end @@ -82,6 +83,19 @@ module OpenFoodNetwork end + def self.refresh_distributor_fee(enterprise_fee) + enterprise_fee.exchange_fees. + joins(:exchange => :order_cycle). + merge(Exchange.outgoing). + merge(OrderCycle.dated). + merge(OrderCycle.not_closed). + each do |exf| + + refresh_cache exf.exchange.receiver, exf.exchange.order_cycle + end + end + + def self.refresh_cache(distributor, order_cycle) Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id end diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index 26f3a9b844..c11565212e 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -179,6 +179,54 @@ module OpenFoodNetwork expect(ProductsCache).to receive(:order_cycle_changed).with(oc).once ProductsCache.enterprise_fee_changed ef_coord end + + + describe "updating exchanges when it's a distributor fee" do + let(:ex0) { create(:exchange, order_cycle: oc, sender: s, receiver: c, incoming: true, enterprise_fees: [ef]) } + let(:ex1) { create(:exchange, order_cycle: oc, sender: c, receiver: d1, incoming: false, enterprise_fees: [ef]) } + let(:ex2) { create(:exchange, order_cycle: oc, sender: c, receiver: d2, incoming: false, enterprise_fees: []) } + + describe "updating distributions that include the fee" do + it "does not update undated order cycles" do + oc.update_attributes! orders_open_at: nil, orders_close_at: nil + ex1 + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).never + ProductsCache.enterprise_fee_changed ef + end + + it "updates upcoming order cycles" do + oc.update_attributes! orders_open_at: 1.week.from_now, orders_close_at: 2.weeks.from_now + ex1 + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.enterprise_fee_changed ef + end + + it "updates open order cycles" do + ex1 + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.enterprise_fee_changed ef + end + + it "does not update closed order cycles" do + oc.update_attributes! orders_open_at: 2.weeks.ago, orders_close_at: 1.week.ago + ex1 + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).never + ProductsCache.enterprise_fee_changed ef + end + end + + it "doesn't update exchanges that don't include the fee" do + ex1; ex2 + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc).never + ProductsCache.enterprise_fee_changed ef + end + + it "doesn't update incoming exchanges" do + ex0 + expect(ProductsCache).to receive(:refresh_cache).with(c, oc).never + ProductsCache.enterprise_fee_changed ef + end + end end describe "refreshing the cache" do From 3bcd3257a1f95a1c7e2e4ea6009d683301311e4a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Jan 2016 15:59:17 +1100 Subject: [PATCH 070/215] Perform refresh of products cache when supplier fee is changed --- lib/open_food_network/products_cache.rb | 29 ++++++++++++++ .../open_food_network/products_cache_spec.rb | 40 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 5013f5b37a..cdd4889f4c 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -50,6 +50,7 @@ module OpenFoodNetwork def self.enterprise_fee_changed(enterprise_fee) + refresh_supplier_fee enterprise_fee refresh_coordinator_fee enterprise_fee refresh_distributor_fee enterprise_fee end @@ -76,6 +77,19 @@ module OpenFoodNetwork end + def self.refresh_supplier_fee(enterprise_fee) + outgoing_exchanges = Set.new + + incoming_exchanges_for_enterprise_fee(enterprise_fee).each do |exchange| + outgoing_exchanges.merge outgoing_exchanges_with_variants(exchange.order_cycle, exchange.variant_ids) + end + + outgoing_exchanges.each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end + end + + def self.refresh_coordinator_fee(enterprise_fee) enterprise_fee.order_cycles.each do |order_cycle| order_cycle_changed order_cycle @@ -96,6 +110,21 @@ module OpenFoodNetwork end + def self.incoming_exchanges_for_enterprise_fee(enterprise_fee) + enterprise_fee.exchanges.incoming. + joins(:order_cycle). + merge(OrderCycle.dated). + merge(OrderCycle.not_closed) + end + + + def self.outgoing_exchanges_with_variants(order_cycle, variant_ids) + order_cycle.exchanges.outgoing. + joins(:exchange_variants). + where('exchange_variants.variant_id IN (?)', variant_ids) + end + + def self.refresh_cache(distributor, order_cycle) Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id end diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index c11565212e..1ee8e032d6 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -174,6 +174,46 @@ module OpenFoodNetwork let(:oc) { create(:open_order_cycle, coordinator: c) } + describe "updating exchanges when it's a supplier fee" do + let(:v) { create(:variant) } + let!(:ex1) { create(:exchange, order_cycle: oc, sender: s, receiver: c, incoming: true, variants: [v], enterprise_fees: [ef]) } + let!(:ex2) { create(:exchange, order_cycle: oc, sender: c, receiver: d1, incoming: false, variants: [v]) } + let!(:ex3) { create(:exchange, order_cycle: oc, sender: c, receiver: d2, incoming: false, variants: []) } + + before { ef.reload } + + describe "updating distributions that include one of the supplier's variants" do + it "does not update undated order cycles" do + oc.update_attributes! orders_open_at: nil, orders_close_at: nil + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).never + ProductsCache.enterprise_fee_changed ef + end + + it "updates upcoming order cycles" do + oc.update_attributes! orders_open_at: 1.week.from_now, orders_close_at: 2.weeks.from_now + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.enterprise_fee_changed ef + end + + it "updates open order cycles" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.enterprise_fee_changed ef + end + + it "does not update closed order cycles" do + oc.update_attributes! orders_open_at: 2.weeks.ago, orders_close_at: 1.week.ago + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).never + ProductsCache.enterprise_fee_changed ef + end + end + + it "doesn't update distributions that don't include any of the supplier's variants" do + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc).never + ProductsCache.enterprise_fee_changed ef + end + end + + it "updates order cycles when it's a coordinator fee" do ef_coord expect(ProductsCache).to receive(:order_cycle_changed).with(oc).once From f756749e02f05a7080e90c78e3fc7076b8ecc069 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Feb 2016 12:28:26 +1100 Subject: [PATCH 071/215] Fix specs --- app/models/enterprise_fee.rb | 4 ++-- app/models/spree/variant_decorator.rb | 2 +- spec/jobs/refresh_products_cache_job_spec.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index e2831a4178..0f4b3f8bc6 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -71,8 +71,8 @@ class EnterpriseFee < ActiveRecord::Base # Remove this association here instead of using dependent: :destroy because # dependent-destroy acts before this around_filter is called, so ProductsCache # has no way of knowing where this fee was used. - order_cycles.clear - exchange_fees.destroy_all + order_cycles(:reload).clear + exchange_fees(:reload).destroy_all # Destroy the enterprise fee yield diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 143508185a..b62b2440b2 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -100,7 +100,7 @@ Spree::Variant.class_eval do # Remove this association here instead of using dependent: :destroy because # dependent-destroy acts before this around_filter is called, so ProductsCache # has no way of knowing which exchanges the variant was a member of. - exchange_variants.destroy_all + exchange_variants(:reload).destroy_all # Destroy the variant yield diff --git a/spec/jobs/refresh_products_cache_job_spec.rb b/spec/jobs/refresh_products_cache_job_spec.rb index 2197ba38c9..d20efe5fad 100644 --- a/spec/jobs/refresh_products_cache_job_spec.rb +++ b/spec/jobs/refresh_products_cache_job_spec.rb @@ -6,11 +6,11 @@ describe RefreshProductsCacheJob do let(:order_cycle) { create(:simple_order_cycle) } it "renders products and writes them to cache" do - OpenFoodNetwork::ProductsRenderer.any_instance.stub(:products_json) { 'products' } + RefreshProductsCacheJob.any_instance.stub(:products_json) { 'products' } run_job RefreshProductsCacheJob.new distributor.id, order_cycle.id - expect(Rails.cache.read("products-json-123-456")).to eq 'products' + expect(Rails.cache.read("products-json-#{distributor.id}-#{order_cycle.id}")).to eq 'products' end describe "fetching products JSON" do From 7c4e9e58385e7e2415a3b08d5eb16c0dd2997001 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Feb 2016 14:22:05 +1100 Subject: [PATCH 072/215] Refresh products cache when product properties are changed --- app/models/spree/product_decorator.rb | 9 ++++---- .../spree/product_property_decorator.rb | 10 +++++++++ app/models/spree/property_decorator.rb | 15 +++++++++++++ spec/models/spree/product_property_spec.rb | 21 +++++++++++++++++++ spec/models/spree/property_spec.rb | 17 +++++++++++++++ 5 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 app/models/spree/product_property_decorator.rb create mode 100644 app/models/spree/property_decorator.rb create mode 100644 spec/models/spree/product_property_spec.rb create mode 100644 spec/models/spree/property_spec.rb diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index e969dd500b..2c51ec5420 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -191,6 +191,11 @@ Spree::Product.class_eval do alias_method_chain :delete, :delete_from_order_cycles + def refresh_products_cache + OpenFoodNetwork::ProductsCache.product_changed self + end + + private def set_available_on_to_now @@ -251,8 +256,4 @@ Spree::Product.class_eval do self.permalink = create_unique_permalink(requested.parameterize) end end - - def refresh_products_cache - OpenFoodNetwork::ProductsCache.product_changed self - end end diff --git a/app/models/spree/product_property_decorator.rb b/app/models/spree/product_property_decorator.rb new file mode 100644 index 0000000000..0f3329c03d --- /dev/null +++ b/app/models/spree/product_property_decorator.rb @@ -0,0 +1,10 @@ +module Spree + ProductProperty.class_eval do + after_save :refresh_products_cache + after_destroy :refresh_products_cache + + def refresh_products_cache + product.refresh_products_cache + end + end +end diff --git a/app/models/spree/property_decorator.rb b/app/models/spree/property_decorator.rb new file mode 100644 index 0000000000..5b8e53338c --- /dev/null +++ b/app/models/spree/property_decorator.rb @@ -0,0 +1,15 @@ +module Spree + Property.class_eval do + after_save :refresh_products_cache + + # When a Property is destroyed, dependent-destroy will destroy all ProductProperties, + # which will take care of refreshing the products cache + + + private + + def refresh_products_cache + product_properties(:reload).each &:refresh_products_cache + end + end +end diff --git a/spec/models/spree/product_property_spec.rb b/spec/models/spree/product_property_spec.rb new file mode 100644 index 0000000000..1c9799ab29 --- /dev/null +++ b/spec/models/spree/product_property_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +module Spree + describe ProductProperty do + describe "callbacks" do + let(:product) { product_property.product } + let(:product_property) { create(:product_property) } + + it "refreshes the products cache on save, via Product" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + product_property.value = 123 + product_property.save + end + + it "refreshes the products cache on destroy" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + product_property.destroy + end + end + end +end diff --git a/spec/models/spree/property_spec.rb b/spec/models/spree/property_spec.rb new file mode 100644 index 0000000000..a86513b5a9 --- /dev/null +++ b/spec/models/spree/property_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +module Spree + describe Property do + describe "callbacks" do + let(:property) { product_property.property } + let(:product) { product_property.product } + let(:product_property) { create(:product_property) } + + it "refreshes the products cache on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + property.name = 'asdf' + property.save + end + end + end +end From 6d80d91873a7c3b86e718d2ca63eb00a75d981c0 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Feb 2016 14:53:55 +1100 Subject: [PATCH 073/215] Refresh products cache when taxons or classifications are changed or destroyed --- app/models/spree/classification_decorator.rb | 9 +++++++++ app/models/spree/taxon_decorator.rb | 12 ++++++++++++ spec/models/spree/classification_spec.rb | 17 +++++++++++++++-- spec/models/spree/taxon_spec.rb | 15 +++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/app/models/spree/classification_decorator.rb b/app/models/spree/classification_decorator.rb index c69eb01fcf..5e9655a907 100644 --- a/app/models/spree/classification_decorator.rb +++ b/app/models/spree/classification_decorator.rb @@ -1,6 +1,15 @@ Spree::Classification.class_eval do belongs_to :product, :class_name => "Spree::Product", touch: true + after_save :refresh_products_cache before_destroy :dont_destroy_if_primary_taxon + after_destroy :refresh_products_cache + + + private + + def refresh_products_cache + product.refresh_products_cache + end def dont_destroy_if_primary_taxon if product.primary_taxon == taxon diff --git a/app/models/spree/taxon_decorator.rb b/app/models/spree/taxon_decorator.rb index 1a26ce73a8..a1e07cc53e 100644 --- a/app/models/spree/taxon_decorator.rb +++ b/app/models/spree/taxon_decorator.rb @@ -1,7 +1,12 @@ Spree::Taxon.class_eval do + has_many :classifications, :dependent => :destroy + + self.attachment_definitions[:icon][:path] = 'public/images/spree/taxons/:id/:style/:basename.:extension' self.attachment_definitions[:icon][:url] = '/images/spree/taxons/:id/:style/:basename.:extension' + after_save :refresh_products_cache + # Indicate which filters should be used for this taxon def applicable_filters @@ -45,4 +50,11 @@ Spree::Taxon.class_eval do taxons end + + + private + + def refresh_products_cache + products(:reload).each &:refresh_products_cache + end end diff --git a/spec/models/spree/classification_spec.rb b/spec/models/spree/classification_spec.rb index f26f6da0c0..44449ee54e 100644 --- a/spec/models/spree/classification_spec.rb +++ b/spec/models/spree/classification_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' module Spree describe Classification do - let(:product) { create(:simple_product) } - let(:taxon) { create(:taxon) } + let!(:product) { create(:simple_product) } + let!(:taxon) { create(:taxon) } let(:classification) { create(:classification, taxon: taxon, product: product) } it "won't destroy if classification is the primary taxon" do @@ -11,5 +11,18 @@ module Spree classification.destroy.should be_false classification.errors.messages[:base].should == ["Taxon #{taxon.name} is the primary taxon of #{product.name} and cannot be deleted"] end + + describe "callbacks" do + it "refreshes the products cache on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + classification + end + + it "refreshes the products cache on destroy" do + classification + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + classification.destroy + end + end end end diff --git a/spec/models/spree/taxon_spec.rb b/spec/models/spree/taxon_spec.rb index a0d729c054..926e5b3c5f 100644 --- a/spec/models/spree/taxon_spec.rb +++ b/spec/models/spree/taxon_spec.rb @@ -7,6 +7,21 @@ module Spree let(:t1) { create(:taxon) } let(:t2) { create(:taxon) } + describe "callbacks" do + let(:product) { create(:simple_product, taxons: [t1]) } + + it "refreshes the products cache on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + t1.name = 'asdf' + t1.save + end + + it "refreshes the products cache on destroy" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + t1.destroy + end + end + describe "finding all supplied taxons" do let!(:p1) { create(:simple_product, supplier: e, taxons: [t1, t2]) } From d8d803546b0b6c5b68f4eba2a685da2d9e90bce0 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Feb 2016 15:34:23 +1100 Subject: [PATCH 074/215] Refresh products cache when master variants or images are changed or destroyed --- app/models/spree/image_decorator.rb | 10 ++++++++++ app/models/spree/variant_decorator.rb | 25 ++++++++++++++++++------- spec/models/spree/image_spec.rb | 18 ++++++++++++++++++ spec/models/spree/variant_spec.rb | 19 +++++++++++++++++++ 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/app/models/spree/image_decorator.rb b/app/models/spree/image_decorator.rb index 7d86aae3a6..24056926b7 100644 --- a/app/models/spree/image_decorator.rb +++ b/app/models/spree/image_decorator.rb @@ -1,4 +1,7 @@ Spree::Image.class_eval do + after_save :refresh_products_cache + after_destroy :refresh_products_cache + # Spree stores attachent definitions in JSON. This converts the style name and format to # strings. However, when paperclip encounters these, it doesn't recognise the format. # Here we solve that problem by converting format and style name to symbols. @@ -20,4 +23,11 @@ Spree::Image.class_eval do end reformat_styles + + + private + + def refresh_products_cache + viewable.try :refresh_products_cache + end end diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index b62b2440b2..9efa89795e 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -92,18 +92,29 @@ Spree::Variant.class_eval do end def refresh_products_cache - OpenFoodNetwork::ProductsCache.variant_changed self + if is_master? + product.refresh_products_cache + else + OpenFoodNetwork::ProductsCache.variant_changed self + end end def destruction - OpenFoodNetwork::ProductsCache.variant_destroyed(self) do - # Remove this association here instead of using dependent: :destroy because - # dependent-destroy acts before this around_filter is called, so ProductsCache - # has no way of knowing which exchanges the variant was a member of. + if is_master? exchange_variants(:reload).destroy_all - - # Destroy the variant yield + product.refresh_products_cache + + else + OpenFoodNetwork::ProductsCache.variant_destroyed(self) do + # Remove this association here instead of using dependent: :destroy because + # dependent-destroy acts before this around_filter is called, so ProductsCache + # has no way of knowing which exchanges the variant was a member of. + exchange_variants(:reload).destroy_all + + # Destroy the variant + yield + end end end end diff --git a/spec/models/spree/image_spec.rb b/spec/models/spree/image_spec.rb index 56665fa641..c9a9c1243c 100644 --- a/spec/models/spree/image_spec.rb +++ b/spec/models/spree/image_spec.rb @@ -14,5 +14,23 @@ module Spree Image.format_styles(formatted).should == {:mini => ["48x48>", :png]} end end + + describe "callbacks" do + let!(:product) { create(:simple_product) } + + let!(:image_file) { File.open("#{Rails.root}/app/assets/images/logo-white.png") } + let!(:image) { Image.create(viewable_id: product.master.id, viewable_type: 'Spree::Variant', alt: "image", attachment: image_file) } + + it "refreshes the products cache when changed" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + image.alt = 'asdf' + image.save + end + + it "refreshes the products cache when destroyed" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + image.destroy + end + end end end diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 73789e62c2..11104a02e2 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -126,6 +126,25 @@ module Spree expect(OpenFoodNetwork::ProductsCache).to receive(:variant_destroyed).with(variant) variant.destroy end + + context "when it is the master variant" do + let(:product) { create(:simple_product) } + let(:master) { product.master } + + it "refreshes the products cache for the entire product on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).never + master.sku = 'abc123' + master.save + end + + it "refreshes the products cache for the entire product on destroy" do + # Does this ever happen? + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_destroyed).never + master.destroy + end + end end describe "indexing variants by id" do From 339f3fc2f0a07db1eb9875ce34d8526557517f3b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Feb 2016 16:08:41 +1100 Subject: [PATCH 075/215] Refresh products cache when price is changed or destroyed --- app/models/spree/price_decorator.rb | 13 +++++++++++++ app/models/spree/variant_decorator.rb | 13 +++++++------ spec/models/spree/price_spec.rb | 21 +++++++++++++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 app/models/spree/price_decorator.rb create mode 100644 spec/models/spree/price_spec.rb diff --git a/app/models/spree/price_decorator.rb b/app/models/spree/price_decorator.rb new file mode 100644 index 0000000000..340a6aa17a --- /dev/null +++ b/app/models/spree/price_decorator.rb @@ -0,0 +1,13 @@ +module Spree + Price.class_eval do + after_save :refresh_products_cache + after_destroy :refresh_products_cache + + + private + + def refresh_products_cache + variant.refresh_products_cache + end + end +end diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 9efa89795e..7be9d712d0 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -85,12 +85,6 @@ Spree::Variant.class_eval do end end - private - - def update_weight_from_unit_value - self.weight = weight_from_unit_value if self.product.variant_unit == 'weight' && unit_value.present? - end - def refresh_products_cache if is_master? product.refresh_products_cache @@ -99,6 +93,13 @@ Spree::Variant.class_eval do end end + + private + + def update_weight_from_unit_value + self.weight = weight_from_unit_value if self.product.variant_unit == 'weight' && unit_value.present? + end + def destruction if is_master? exchange_variants(:reload).destroy_all diff --git a/spec/models/spree/price_spec.rb b/spec/models/spree/price_spec.rb new file mode 100644 index 0000000000..46f6653c77 --- /dev/null +++ b/spec/models/spree/price_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +module Spree + describe Price do + describe "callbacks" do + let(:variant) { create(:variant) } + let(:price) { variant.default_price } + + it "refreshes the products cache on change" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).with(variant) + price.amount = 123 + price.save + end + + it "refreshes the products cache on destroy" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).with(variant) + price.destroy + end + end + end +end From d0b7b4ee50f2a34ae5b3f7304db5de2817ff6bce Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 11:22:37 +1100 Subject: [PATCH 076/215] Add CachedProductsRenderer - wraps ProductsRenderer using Rails cache --- .../cached_products_renderer.rb | 35 ++++++++ .../cached_products_renderer_spec.rb | 80 +++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 lib/open_food_network/cached_products_renderer.rb create mode 100644 spec/lib/open_food_network/cached_products_renderer_spec.rb diff --git a/lib/open_food_network/cached_products_renderer.rb b/lib/open_food_network/cached_products_renderer.rb new file mode 100644 index 0000000000..9a42f4e5e4 --- /dev/null +++ b/lib/open_food_network/cached_products_renderer.rb @@ -0,0 +1,35 @@ +require 'open_food_network/products_renderer' + +# Wrapper for ProductsRenderer that caches the JSON output. +# ProductsRenderer::NoProducts is represented in the cache as nil, +# but re-raised to provide the same interface as ProductsRenderer. + +module OpenFoodNetwork + class CachedProductsRenderer + def initialize(distributor, order_cycle) + @distributor = distributor + @order_cycle = order_cycle + end + + def products_json + products_json = Rails.cache.fetch("products-json-#{@distributor.id}-#{@order_cycle.id}") do + begin + uncached_products_json + rescue ProductsRenderer::NoProducts + nil + end + end + + raise ProductsRenderer::NoProducts.new if products_json.nil? + + products_json + end + + + private + + def uncached_products_json + ProductsRenderer.new(@distributor, @order_cycle).products_json + end + end +end diff --git a/spec/lib/open_food_network/cached_products_renderer_spec.rb b/spec/lib/open_food_network/cached_products_renderer_spec.rb new file mode 100644 index 0000000000..6f52dee2cd --- /dev/null +++ b/spec/lib/open_food_network/cached_products_renderer_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' +require 'open_food_network/cached_products_renderer' +require 'open_food_network/products_renderer' + +module OpenFoodNetwork + describe CachedProductsRenderer do + let(:distributor) { double(:distributor, id: 123) } + let(:order_cycle) { double(:order_cycle, id: 456) } + let(:cpr) { CachedProductsRenderer.new(distributor, order_cycle) } + + describe "when the products JSON is already cached" do + before do + Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", 'products' + end + + it "returns the cached JSON" do + expect(cpr.products_json).to eq 'products' + end + + it "raises an exception when there are no products" do + Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", nil + expect { cpr.products_json }.to raise_error ProductsRenderer::NoProducts + end + end + + describe "when the products JSON is not cached" do + let(:cached_json) { Rails.cache.read "products-json-#{distributor.id}-#{order_cycle.id}" } + let(:cache_present) { Rails.cache.exist? "products-json-#{distributor.id}-#{order_cycle.id}" } + + before do + Rails.cache.clear + cpr.stub(:uncached_products_json) { 'fresh products' } + end + + describe "when there are products" do + it "returns products as JSON" do + expect(cpr.products_json).to eq 'fresh products' + end + + it "caches the JSON" do + cpr.products_json + expect(cached_json).to eq 'fresh products' + end + end + + describe "when there are no products" do + before { cpr.stub(:uncached_products_json).and_raise ProductsRenderer::NoProducts } + + it "raises an error" do + expect { cpr.products_json }.to raise_error ProductsRenderer::NoProducts + end + + it "caches the products as nil" do + expect { cpr.products_json }.to raise_error ProductsRenderer::NoProducts + expect(cache_present).to be + expect(cached_json).to be_nil + end + end + + describe "logging a warning" do + it "logs a warning when in production" + it "logs a warning when in staging" + it "does not log a warning in development" + it "does not log a warning in test" + end + end + + describe "fetching uncached products from ProductsRenderer" do + let(:pr) { double(:products_renderer, products_json: 'uncached products') } + + before do + ProductsRenderer.stub(:new) { pr } + end + + it "returns the uncached products" do + expect(cpr.send(:uncached_products_json)).to eq 'uncached products' + end + end + end +end From ff493c21d40b3c3a0c5b7bae3b0731b07ce3e1b8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 11:33:59 +1100 Subject: [PATCH 077/215] Log a warning on cache MISS --- .../cached_products_renderer.rb | 8 +++++ .../cached_products_renderer_spec.rb | 33 ++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/open_food_network/cached_products_renderer.rb b/lib/open_food_network/cached_products_renderer.rb index 9a42f4e5e4..ef11dfaba5 100644 --- a/lib/open_food_network/cached_products_renderer.rb +++ b/lib/open_food_network/cached_products_renderer.rb @@ -13,6 +13,8 @@ module OpenFoodNetwork def products_json products_json = Rails.cache.fetch("products-json-#{@distributor.id}-#{@order_cycle.id}") do + log_warning + begin uncached_products_json rescue ProductsRenderer::NoProducts @@ -28,6 +30,12 @@ module OpenFoodNetwork private + def log_warning + if Rails.env.production? || Rails.env.staging? + Bugsnag.notify RuntimeError.new("Live server MISS on products cache for distributor: #{@distributor.id}, order cycle: #{@order_cycle.id}") + end + end + def uncached_products_json ProductsRenderer.new(@distributor, @order_cycle).products_json end diff --git a/spec/lib/open_food_network/cached_products_renderer_spec.rb b/spec/lib/open_food_network/cached_products_renderer_spec.rb index 6f52dee2cd..9983e79e16 100644 --- a/spec/lib/open_food_network/cached_products_renderer_spec.rb +++ b/spec/lib/open_food_network/cached_products_renderer_spec.rb @@ -41,6 +41,11 @@ module OpenFoodNetwork cpr.products_json expect(cached_json).to eq 'fresh products' end + + it "logs a warning" do + cpr.should_receive :log_warning + cpr.products_json + end end describe "when there are no products" do @@ -55,13 +60,31 @@ module OpenFoodNetwork expect(cache_present).to be expect(cached_json).to be_nil end + + it "logs a warning" do + cpr.should_receive :log_warning + expect { cpr.products_json }.to raise_error ProductsRenderer::NoProducts + end + end + end + + describe "logging a warning" do + it "logs a warning when in production" do + Rails.env.stub(:production?) { true } + expect(Bugsnag).to receive(:notify) + cpr.send(:log_warning) end - describe "logging a warning" do - it "logs a warning when in production" - it "logs a warning when in staging" - it "does not log a warning in development" - it "does not log a warning in test" + it "logs a warning when in staging" do + Rails.env.stub(:production?) { false } + Rails.env.stub(:staging?) { true } + expect(Bugsnag).to receive(:notify) + cpr.send(:log_warning) + end + + it "does not log a warning in development or test" do + expect(Bugsnag).to receive(:notify).never + cpr.send(:log_warning) end end From 235c4638498f5e2a08ec137555ac02ebd1eb4409 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 11:38:09 +1100 Subject: [PATCH 078/215] Hide wrapped exception, too --- lib/open_food_network/cached_products_renderer.rb | 4 +++- .../open_food_network/cached_products_renderer_spec.rb | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/open_food_network/cached_products_renderer.rb b/lib/open_food_network/cached_products_renderer.rb index ef11dfaba5..eef3d000b2 100644 --- a/lib/open_food_network/cached_products_renderer.rb +++ b/lib/open_food_network/cached_products_renderer.rb @@ -6,6 +6,8 @@ require 'open_food_network/products_renderer' module OpenFoodNetwork class CachedProductsRenderer + class NoProducts < Exception; end + def initialize(distributor, order_cycle) @distributor = distributor @order_cycle = order_cycle @@ -22,7 +24,7 @@ module OpenFoodNetwork end end - raise ProductsRenderer::NoProducts.new if products_json.nil? + raise NoProducts.new if products_json.nil? products_json end diff --git a/spec/lib/open_food_network/cached_products_renderer_spec.rb b/spec/lib/open_food_network/cached_products_renderer_spec.rb index 9983e79e16..a203d66c1d 100644 --- a/spec/lib/open_food_network/cached_products_renderer_spec.rb +++ b/spec/lib/open_food_network/cached_products_renderer_spec.rb @@ -19,7 +19,7 @@ module OpenFoodNetwork it "raises an exception when there are no products" do Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", nil - expect { cpr.products_json }.to raise_error ProductsRenderer::NoProducts + expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts end end @@ -52,18 +52,18 @@ module OpenFoodNetwork before { cpr.stub(:uncached_products_json).and_raise ProductsRenderer::NoProducts } it "raises an error" do - expect { cpr.products_json }.to raise_error ProductsRenderer::NoProducts + expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts end it "caches the products as nil" do - expect { cpr.products_json }.to raise_error ProductsRenderer::NoProducts + expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts expect(cache_present).to be expect(cached_json).to be_nil end it "logs a warning" do cpr.should_receive :log_warning - expect { cpr.products_json }.to raise_error ProductsRenderer::NoProducts + expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts end end end From fa543fed63813cc2c61f90595ecea75e88cb504b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 11:41:56 +1100 Subject: [PATCH 079/215] Deal with unset distribution --- lib/open_food_network/cached_products_renderer.rb | 2 ++ .../open_food_network/cached_products_renderer_spec.rb | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/lib/open_food_network/cached_products_renderer.rb b/lib/open_food_network/cached_products_renderer.rb index eef3d000b2..2174bf4094 100644 --- a/lib/open_food_network/cached_products_renderer.rb +++ b/lib/open_food_network/cached_products_renderer.rb @@ -14,6 +14,8 @@ module OpenFoodNetwork end def products_json + raise NoProducts.new if @distributor.nil? || @order_cycle.nil? + products_json = Rails.cache.fetch("products-json-#{@distributor.id}-#{@order_cycle.id}") do log_warning diff --git a/spec/lib/open_food_network/cached_products_renderer_spec.rb b/spec/lib/open_food_network/cached_products_renderer_spec.rb index a203d66c1d..03b0ab05d5 100644 --- a/spec/lib/open_food_network/cached_products_renderer_spec.rb +++ b/spec/lib/open_food_network/cached_products_renderer_spec.rb @@ -8,6 +8,14 @@ module OpenFoodNetwork let(:order_cycle) { double(:order_cycle, id: 456) } let(:cpr) { CachedProductsRenderer.new(distributor, order_cycle) } + describe "when the distribution is not set" do + let(:cpr) { CachedProductsRenderer.new(nil, nil) } + + it "raises an exception and returns no products" do + expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts + end + end + describe "when the products JSON is already cached" do before do Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", 'products' From 2f602f2a57d9cc602970ae5f3a0183b57c28986c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 11:42:25 +1100 Subject: [PATCH 080/215] Shop controller uses CachedProductsRenderer --- app/controllers/shop_controller.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index d1c7e7582e..55b6bcc02b 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -1,4 +1,4 @@ -require 'open_food_network/products_renderer' +require 'open_food_network/cached_products_renderer' class ShopController < BaseController layout "darkswarm" @@ -11,11 +11,11 @@ class ShopController < BaseController def products begin - products_json = OpenFoodNetwork::ProductsRenderer.new(current_distributor, current_order_cycle).products_json + products_json = OpenFoodNetwork::CachedProductsRenderer.new(current_distributor, current_order_cycle).products_json render json: products_json - rescue OpenFoodNetwork::ProductsRenderer::NoProducts + rescue OpenFoodNetwork::CachedProductsRenderer::NoProducts render status: 404, json: '' end end From f78826c9c7f376f5f98e83582a1328ceb55695ea Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 12:05:42 +1100 Subject: [PATCH 081/215] Fix rare case where price is saved without variant --- app/models/spree/price_decorator.rb | 2 +- spec/models/spree/price_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/models/spree/price_decorator.rb b/app/models/spree/price_decorator.rb index 340a6aa17a..0dd3a5a0c6 100644 --- a/app/models/spree/price_decorator.rb +++ b/app/models/spree/price_decorator.rb @@ -7,7 +7,7 @@ module Spree private def refresh_products_cache - variant.refresh_products_cache + variant.andand.refresh_products_cache end end end diff --git a/spec/models/spree/price_spec.rb b/spec/models/spree/price_spec.rb index 46f6653c77..378925f052 100644 --- a/spec/models/spree/price_spec.rb +++ b/spec/models/spree/price_spec.rb @@ -16,6 +16,12 @@ module Spree expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).with(variant) price.destroy end + + it "does not refresh the cache when variant is not set" do + # Creates a price without the back link to variant + create(:product, master: create(:variant)) + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).never + end end end end From 62c6530ca93319fd88d36e01f463762bbd246748 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 12:15:59 +1100 Subject: [PATCH 082/215] Do not refresh products cache when price destroyed - variant destruction is main (only?) trigger, it causes refresh --- app/models/spree/price_decorator.rb | 1 - spec/models/spree/price_spec.rb | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/models/spree/price_decorator.rb b/app/models/spree/price_decorator.rb index 0dd3a5a0c6..f50a86066b 100644 --- a/app/models/spree/price_decorator.rb +++ b/app/models/spree/price_decorator.rb @@ -1,7 +1,6 @@ module Spree Price.class_eval do after_save :refresh_products_cache - after_destroy :refresh_products_cache private diff --git a/spec/models/spree/price_spec.rb b/spec/models/spree/price_spec.rb index 378925f052..5d48afd95b 100644 --- a/spec/models/spree/price_spec.rb +++ b/spec/models/spree/price_spec.rb @@ -12,10 +12,8 @@ module Spree price.save end - it "refreshes the products cache on destroy" do - expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).with(variant) - price.destroy - end + # Do not refresh on price destruction - this (only?) happens when variant is destroyed, + # and in that case the variant will take responsibility for refreshing the cache it "does not refresh the cache when variant is not set" do # Creates a price without the back link to variant From 540687515e6e46e510abb898e072ea8e325a5798 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 12:29:16 +1100 Subject: [PATCH 083/215] Reify coordinator_fees HABTM join table as CoordinatorFee model using HMT --- app/models/coordinator_fee.rb | 4 ++++ app/models/enterprise_fee.rb | 7 ++++++- app/models/order_cycle.rb | 4 +++- db/migrate/20160204013914_add_id_to_coordinator_fees.rb | 5 +++++ db/schema.rb | 4 ++-- 5 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 app/models/coordinator_fee.rb create mode 100644 db/migrate/20160204013914_add_id_to_coordinator_fees.rb diff --git a/app/models/coordinator_fee.rb b/app/models/coordinator_fee.rb new file mode 100644 index 0000000000..f15a194158 --- /dev/null +++ b/app/models/coordinator_fee.rb @@ -0,0 +1,4 @@ +class CoordinatorFee < ActiveRecord::Base + belongs_to :order_cycle + belongs_to :enterprise_fee +end diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 0f4b3f8bc6..8e45707d07 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -1,13 +1,18 @@ class EnterpriseFee < ActiveRecord::Base belongs_to :enterprise belongs_to :tax_category, class_name: 'Spree::TaxCategory', foreign_key: 'tax_category_id' - has_and_belongs_to_many :order_cycles, join_table: 'coordinator_fees' + + has_many :coordinator_fees + has_many :order_cycles, through: :coordinator_fees + has_many :exchange_fees has_many :exchanges, through: :exchange_fees + after_save :refresh_products_cache around_destroy :destruction + calculated_adjustments attr_accessible :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index d3f11860e9..8d0fa83da8 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -1,6 +1,8 @@ class OrderCycle < ActiveRecord::Base belongs_to :coordinator, :class_name => 'Enterprise' - has_and_belongs_to_many :coordinator_fees, :class_name => 'EnterpriseFee', :join_table => 'coordinator_fees' + + has_many :coordinator_fee_refs, class_name: 'CoordinatorFee' + has_many :coordinator_fees, through: :coordinator_fee_refs, source: :enterprise_fee has_many :exchanges, :dependent => :destroy diff --git a/db/migrate/20160204013914_add_id_to_coordinator_fees.rb b/db/migrate/20160204013914_add_id_to_coordinator_fees.rb new file mode 100644 index 0000000000..74326a6006 --- /dev/null +++ b/db/migrate/20160204013914_add_id_to_coordinator_fees.rb @@ -0,0 +1,5 @@ +class AddIdToCoordinatorFees < ActiveRecord::Migration + def change + add_column :coordinator_fees, :id, :primary_key + end +end diff --git a/db/schema.rb b/db/schema.rb index 465530b949..9912a89c63 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 => 20151128185900) do +ActiveRecord::Schema.define(:version => 20160204013914) do create_table "account_invoices", :force => true do |t| t.integer "user_id", :null => false @@ -176,7 +176,7 @@ ActiveRecord::Schema.define(:version => 20151128185900) do add_index "cms_snippets", ["site_id", "identifier"], :name => "index_cms_snippets_on_site_id_and_identifier", :unique => true add_index "cms_snippets", ["site_id", "position"], :name => "index_cms_snippets_on_site_id_and_position" - create_table "coordinator_fees", :id => false, :force => true do |t| + create_table "coordinator_fees", :force => true do |t| t.integer "order_cycle_id" t.integer "enterprise_fee_id" end From 0a90a48b04a6da2f50e55306885c2796b4a4f31d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 12:45:05 +1100 Subject: [PATCH 084/215] Refresh products cache when coordinator fees are changed or destroyed --- app/models/coordinator_fee.rb | 11 +++++++++++ app/models/order_cycle.rb | 9 ++++----- spec/models/coordinator_fee_spec.rb | 19 +++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 spec/models/coordinator_fee_spec.rb diff --git a/app/models/coordinator_fee.rb b/app/models/coordinator_fee.rb index f15a194158..135ee821a9 100644 --- a/app/models/coordinator_fee.rb +++ b/app/models/coordinator_fee.rb @@ -1,4 +1,15 @@ class CoordinatorFee < ActiveRecord::Base belongs_to :order_cycle belongs_to :enterprise_fee + + after_save :refresh_products_cache + after_destroy :refresh_products_cache + + + private + + def refresh_products_cache + order_cycle.refresh_products_cache + end + end diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 8d0fa83da8..58ec1bdfa0 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -241,6 +241,10 @@ class OrderCycle < ActiveRecord::Base coordinator.users.include? user end + def refresh_products_cache + OpenFoodNetwork::ProductsCache.order_cycle_changed self + end + private @@ -254,9 +258,4 @@ class OrderCycle < ActiveRecord::Base distributed_variants.include?(product.master) && (product.variants & distributed_variants).empty? end - - def refresh_products_cache - OpenFoodNetwork::ProductsCache.order_cycle_changed self - end - end diff --git a/spec/models/coordinator_fee_spec.rb b/spec/models/coordinator_fee_spec.rb new file mode 100644 index 0000000000..2a54a0e96c --- /dev/null +++ b/spec/models/coordinator_fee_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe CoordinatorFee do + describe "products caching" do + let(:order_cycle) { create(:simple_order_cycle) } + let(:enterprise_fee) { create(:enterprise_fee) } + + it "refreshes the products cache on change" do + expect(OpenFoodNetwork::ProductsCache).to receive(:order_cycle_changed).with(order_cycle) + order_cycle.coordinator_fees << enterprise_fee + end + + it "refreshes the products cache on destruction" do + order_cycle.coordinator_fees << enterprise_fee + expect(OpenFoodNetwork::ProductsCache).to receive(:order_cycle_changed).with(order_cycle) + order_cycle.coordinator_fee_refs.first.destroy + end + end +end From a64a501dbb2600190f78b8ac1ed18cb08b196f55 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Feb 2016 15:15:49 +1100 Subject: [PATCH 085/215] Smarter job queuing: Do not enqueue a RefreshProductsCacheJob if there's already one waiting to run --- lib/open_food_network/products_cache.rb | 9 +-- .../products_cache_refreshment.rb | 47 ++++++++++++++ .../products_cache_refreshment_spec.rb | 62 +++++++++++++++++++ .../open_food_network/products_cache_spec.rb | 11 ++-- 4 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 lib/open_food_network/products_cache_refreshment.rb create mode 100644 spec/lib/open_food_network/products_cache_refreshment_spec.rb diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index cdd4889f4c..50a6099f4b 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -1,7 +1,8 @@ -module OpenFoodNetwork +require 'open_food_network/products_cache_refreshment' - # When elements of the data model change, enqueue jobs to refresh the appropriate parts of - # the products cache. +# When elements of the data model change, refresh the appropriate parts of the products cache. + +module OpenFoodNetwork class ProductsCache def self.variant_changed(variant) exchanges_featuring_variants(variant).each do |exchange| @@ -126,7 +127,7 @@ module OpenFoodNetwork def self.refresh_cache(distributor, order_cycle) - Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + ProductsCacheRefreshment.refresh distributor, order_cycle end end end diff --git a/lib/open_food_network/products_cache_refreshment.rb b/lib/open_food_network/products_cache_refreshment.rb new file mode 100644 index 0000000000..68ed5365c1 --- /dev/null +++ b/lib/open_food_network/products_cache_refreshment.rb @@ -0,0 +1,47 @@ +# When enqueuing a job to refresh the products cache for a particular distribution, there +# is no benefit in having more than one job waiting in the queue to be run. + +# Imagine that an admin updates a product. This calls for the products cache to be +# updated, otherwise customers will see stale data. + +# Now while that update is running, the admin makes another change to the product. Since this change +# has been made after the previous update started running, the already-running update will not +# include that change - we need another job. So we enqueue another one. + +# Before that job starts running, our zealous admin makes yet another change. This time, there +# is a job running *and* there is a job that has not yet started to run. In this case, there's no +# benefit in enqueuing another job. When the previously enqueued job starts running, it will pick up +# our admin's update and include it. So we ignore this change (from a cache refreshment perspective) +# and go home happy to have saved our job worker's time. + +module OpenFoodNetwork + class ProductsCacheRefreshment + def self.refresh(distributor, order_cycle) + unless pending_job? distributor, order_cycle + enqueue_job distributor, order_cycle + end + end + + + private + + def self.pending_job?(distributor, order_cycle) + # To inspect each job, we need to deserialize the payload. + # This is slow, and if it's a problem in practice, we could pre-filter in SQL + # for handlers matching the class name, distributor id and order cycle id. + + Delayed::Job. + where(locked_at: nil). + map(&:payload_object). + select { |j| + j.class == RefreshProductsCacheJob && + j.distributor_id == distributor.id && + j.order_cycle_id == order_cycle.id + }.any? + end + + def self.enqueue_job(distributor, order_cycle) + Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + end + end +end diff --git a/spec/lib/open_food_network/products_cache_refreshment_spec.rb b/spec/lib/open_food_network/products_cache_refreshment_spec.rb new file mode 100644 index 0000000000..bb033cd041 --- /dev/null +++ b/spec/lib/open_food_network/products_cache_refreshment_spec.rb @@ -0,0 +1,62 @@ +require 'open_food_network/products_cache_refreshment' + +module OpenFoodNetwork + describe ProductsCacheRefreshment do + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle) } + + before { Delayed::Job.destroy_all } + + describe "when there are no tasks enqueued" do + it "enqueues the task" do + expect do + ProductsCacheRefreshment.refresh distributor, order_cycle + end.to enqueue_job RefreshProductsCacheJob + end + end + + describe "when there is an enqueued task, and it is running" do + before do + job = Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + job.update_attributes! locked_by: 'asdf', locked_at: Time.now + end + + it "enqueues another task" do + expect do + ProductsCacheRefreshment.refresh distributor, order_cycle + end.to enqueue_job RefreshProductsCacheJob + end + end + + describe "when there are two enqueued tasks, and one is running" do + before do + job1 = Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + job1.update_attributes! locked_by: 'asdf', locked_at: Time.now + job2 = Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + end + + it "does not enqueue another task" do + expect do + ProductsCacheRefreshment.refresh distributor, order_cycle + end.not_to enqueue_job RefreshProductsCacheJob + end + end + + describe "enqueuing tasks with different distributions" do + let(:distributor2) { create(:distributor_enterprise) } + let(:order_cycle2) { create(:simple_order_cycle) } + + before do + job1 = Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + job1.update_attributes! locked_by: 'asdf', locked_at: Time.now + job2 = Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + end + + it "ignores tasks with differing distributions when choosing whether to enqueue a job" do + expect do + ProductsCacheRefreshment.refresh distributor2, order_cycle2 + end.to enqueue_job RefreshProductsCacheJob + end + end + end +end diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index 1ee8e032d6..56b557a7ee 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -270,13 +270,12 @@ module OpenFoodNetwork end describe "refreshing the cache" do - let(:distributor) { double(:distributor, id: 123) } - let(:order_cycle) { double(:order_cycle, id: 456) } + let(:distributor) { double(:distributor) } + let(:order_cycle) { double(:order_cycle) } - it "enqueues a RefreshProductsCacheJob" do - expect do - ProductsCache.send(:refresh_cache, distributor, order_cycle) - end.to enqueue_job RefreshProductsCacheJob, distributor_id: distributor.id, order_cycle_id: order_cycle.id + it "notifies ProductsCacheRefreshment" do + expect(ProductsCacheRefreshment).to receive(:refresh).with(distributor, order_cycle) + ProductsCache.send(:refresh_cache, distributor, order_cycle) end end end From 8bd5a36aaff4e020f7cdb414f0305b08e3f2947d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 5 Feb 2016 09:14:49 +1100 Subject: [PATCH 086/215] Remove enterprise fee destruction cache callback - responsibility to be handled by dependent models --- app/models/enterprise_fee.rb | 21 ++++----------------- lib/open_food_network/products_cache.rb | 5 ----- spec/models/enterprise_fee_spec.rb | 5 ----- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 8e45707d07..6e26b9f5fa 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -2,15 +2,16 @@ class EnterpriseFee < ActiveRecord::Base belongs_to :enterprise belongs_to :tax_category, class_name: 'Spree::TaxCategory', foreign_key: 'tax_category_id' - has_many :coordinator_fees + has_many :coordinator_fees, dependent: :destroy has_many :order_cycles, through: :coordinator_fees - has_many :exchange_fees + has_many :exchange_fees, dependent: :destroy has_many :exchanges, through: :exchange_fees after_save :refresh_products_cache - around_destroy :destruction + # After destroy, the products cache is refreshed via the after_destroy hook for + # coordinator_fees and exchange_fees calculated_adjustments @@ -70,18 +71,4 @@ class EnterpriseFee < ActiveRecord::Base def refresh_products_cache OpenFoodNetwork::ProductsCache.enterprise_fee_changed self end - - def destruction - OpenFoodNetwork::ProductsCache.enterprise_fee_destroyed(self) do - # Remove this association here instead of using dependent: :destroy because - # dependent-destroy acts before this around_filter is called, so ProductsCache - # has no way of knowing where this fee was used. - order_cycles(:reload).clear - exchange_fees(:reload).destroy_all - - # Destroy the enterprise fee - yield - end - - end end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 50a6099f4b..d01bcf86e3 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -57,11 +57,6 @@ module OpenFoodNetwork end - def self.enterprise_fee_destroyed(enterprise_fee, &block) - block.call - end - - private def self.exchanges_featuring_variants(variants, distributor: nil) diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb index 7140feda4a..109e5baac7 100644 --- a/spec/models/enterprise_fee_spec.rb +++ b/spec/models/enterprise_fee_spec.rb @@ -18,11 +18,6 @@ describe EnterpriseFee do ef.save end - it "refreshes the products cache when destroyed" do - expect(OpenFoodNetwork::ProductsCache).to receive(:enterprise_fee_destroyed).with(ef) - ef.destroy - end - it "removes itself from order cycle coordinator fees when destroyed" do oc = create(:simple_order_cycle, coordinator_fees: [ef]) From 146797ea613eea7b884be3ea307fe3f21c6f33d2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 5 Feb 2016 09:37:10 +1100 Subject: [PATCH 087/215] Generalise method for reuse --- lib/open_food_network/products_cache.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index d01bcf86e3..b1d63c0b9a 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -76,7 +76,7 @@ module OpenFoodNetwork def self.refresh_supplier_fee(enterprise_fee) outgoing_exchanges = Set.new - incoming_exchanges_for_enterprise_fee(enterprise_fee).each do |exchange| + incoming_exchanges(enterprise_fee.exchanges).each do |exchange| outgoing_exchanges.merge outgoing_exchanges_with_variants(exchange.order_cycle, exchange.variant_ids) end @@ -106,8 +106,9 @@ module OpenFoodNetwork end - def self.incoming_exchanges_for_enterprise_fee(enterprise_fee) - enterprise_fee.exchanges.incoming. + def self.incoming_exchanges(exchanges) + exchanges. + incoming. joins(:order_cycle). merge(OrderCycle.dated). merge(OrderCycle.not_closed) From 8af6866ae4775fc614fe7ab72d7500fd575d6adf Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 5 Feb 2016 10:37:05 +1100 Subject: [PATCH 088/215] Refresh products cache when exchange is changed or destroyed --- app/models/exchange.rb | 10 +++ lib/open_food_network/products_cache.rb | 38 ++++++++--- .../open_food_network/products_cache_spec.rb | 65 +++++++++++++++++++ spec/models/exchange_spec.rb | 15 +++++ 4 files changed, 120 insertions(+), 8 deletions(-) diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 137924f7ac..94aada7e0e 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -13,6 +13,9 @@ class Exchange < ActiveRecord::Base validates_presence_of :order_cycle, :sender, :receiver validates_uniqueness_of :sender_id, :scope => [:order_cycle_id, :receiver_id, :incoming] + after_save :refresh_products_cache + after_destroy :refresh_products_cache_from_destroy + accepts_nested_attributes_for :variants scope :in_order_cycle, lambda { |order_cycle| where(order_cycle_id: order_cycle) } @@ -75,4 +78,11 @@ class Exchange < ActiveRecord::Base end end + def refresh_products_cache + OpenFoodNetwork::ProductsCache.exchange_changed self + end + + def refresh_products_cache_from_destroy + OpenFoodNetwork::ProductsCache.exchange_destroyed self + end end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index b1d63c0b9a..d1ce117734 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -50,6 +50,20 @@ module OpenFoodNetwork end + def self.exchange_changed(exchange) + if exchange.incoming + refresh_incoming_exchanges(Exchange.where(id: exchange)) + else + refresh_outgoing_exchange(exchange) + end + end + + + def self.exchange_destroyed(exchange) + exchange_changed exchange + end + + def self.enterprise_fee_changed(enterprise_fee) refresh_supplier_fee enterprise_fee refresh_coordinator_fee enterprise_fee @@ -73,19 +87,27 @@ module OpenFoodNetwork end - def self.refresh_supplier_fee(enterprise_fee) - outgoing_exchanges = Set.new - - incoming_exchanges(enterprise_fee.exchanges).each do |exchange| - outgoing_exchanges.merge outgoing_exchanges_with_variants(exchange.order_cycle, exchange.variant_ids) - end - - outgoing_exchanges.each do |exchange| + def self.refresh_incoming_exchanges(exchanges) + incoming_exchanges(exchanges).map do |exchange| + outgoing_exchanges_with_variants(exchange.order_cycle, exchange.variant_ids) + end.flatten.uniq.each do |exchange| refresh_cache exchange.receiver, exchange.order_cycle end end + def self.refresh_outgoing_exchange(exchange) + if exchange.order_cycle.dated? && !exchange.order_cycle.closed? + refresh_cache exchange.receiver, exchange.order_cycle + end + end + + + def self.refresh_supplier_fee(enterprise_fee) + refresh_incoming_exchanges(enterprise_fee.exchanges) + end + + def self.refresh_coordinator_fee(enterprise_fee) enterprise_fee.order_cycles.each do |order_cycle| order_cycle_changed order_cycle diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index 56b557a7ee..6c5d3019d3 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -164,6 +164,71 @@ module OpenFoodNetwork end + describe "when an exchange is changed" do + let(:s) { create(:supplier_enterprise) } + let(:c) { create(:distributor_enterprise) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let(:v) { create(:variant) } + let(:oc) { create(:open_order_cycle, coordinator: c) } + + describe "incoming exchanges" do + let!(:ex1) { create(:exchange, order_cycle: oc, sender: s, receiver: c, incoming: true, variants: [v]) } + let!(:ex2) { create(:exchange, order_cycle: oc, sender: c, receiver: d1, incoming: false, variants: [v]) } + let!(:ex3) { create(:exchange, order_cycle: oc, sender: c, receiver: d2, incoming: false, variants: []) } + + before { oc.reload } + + it "updates distributions that include one of the supplier's variants" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.exchange_changed ex1 + end + + it "doesn't update distributions that don't include any of the supplier's variants" do + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc).never + ProductsCache.exchange_changed ex1 + end + end + + describe "outgoing exchanges" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: c, receiver: d1, incoming: false) } + + it "does not update for undated order cycles" do + oc.update_attributes! orders_open_at: nil, orders_close_at: nil + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).never + ProductsCache.exchange_changed ex + end + + it "updates for upcoming order cycles" do + oc.update_attributes! orders_open_at: 1.week.from_now, orders_close_at: 2.weeks.from_now + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.exchange_changed ex + end + + it "updates for open order cycles" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.exchange_changed ex + end + + it "does not update for closed order cycles" do + oc.update_attributes! orders_open_at: 2.weeks.ago, orders_close_at: 1.week.ago + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).never + ProductsCache.exchange_changed ex + end + end + end + + + describe "when an exchange is destroyed" do + let(:exchange) { double(:exchange) } + + it "triggers the same update as a change to the exchange" do + expect(ProductsCache).to receive(:exchange_changed).with(exchange) + ProductsCache.exchange_destroyed exchange + end + end + + describe "when an enterprise fee is changed" do let(:s) { create(:supplier_enterprise) } let(:c) { create(:distributor_enterprise) } diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index ac6b7681a8..4b8f0ed1a3 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -91,6 +91,21 @@ describe Exchange do end end + describe "products caching" do + let!(:exchange) { create(:exchange) } + + it "refreshes the products cache on change" do + expect(OpenFoodNetwork::ProductsCache).to receive(:exchange_changed).with(exchange) + exchange.pickup_time = 'asdf' + exchange.save + end + + it "refreshes the products cache on destruction" do + expect(OpenFoodNetwork::ProductsCache).to receive(:exchange_destroyed).with(exchange) + exchange.destroy + end + end + describe "scopes" do let(:supplier) { create(:supplier_enterprise) } let(:coordinator) { create(:distributor_enterprise, is_primary_producer: true) } From 8b070fddbbd79bc08ad712dfc1a815831e392d4c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 5 Feb 2016 10:40:27 +1100 Subject: [PATCH 089/215] Refresh products cache when exchange fee is changed or destroyed --- app/models/exchange_fee.rb | 11 +++++++++++ spec/models/exchange_fee_spec.rb | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 spec/models/exchange_fee_spec.rb diff --git a/app/models/exchange_fee.rb b/app/models/exchange_fee.rb index ff9f12e8dd..03fce8ce04 100644 --- a/app/models/exchange_fee.rb +++ b/app/models/exchange_fee.rb @@ -1,4 +1,15 @@ class ExchangeFee < ActiveRecord::Base belongs_to :exchange belongs_to :enterprise_fee + + + after_save :refresh_products_cache + after_destroy :refresh_products_cache + + + private + + def refresh_products_cache + exchange.refresh_products_cache + end end diff --git a/spec/models/exchange_fee_spec.rb b/spec/models/exchange_fee_spec.rb new file mode 100644 index 0000000000..12bcd0c5e3 --- /dev/null +++ b/spec/models/exchange_fee_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe ExchangeFee do + describe "products caching" do + let(:exchange) { create(:exchange) } + let(:enterprise_fee) { create(:enterprise_fee) } + + it "refreshes the products cache on change" do + expect(OpenFoodNetwork::ProductsCache).to receive(:exchange_changed).with(exchange) + exchange.enterprise_fees << enterprise_fee + end + + it "refreshes the products cache on destruction" do + exchange.enterprise_fees << enterprise_fee + expect(OpenFoodNetwork::ProductsCache).to receive(:exchange_changed).with(exchange) + exchange.reload.exchange_fees.destroy_all + end + end +end From 98961fef74395383e1106d35dea77f7f51938783 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 5 Feb 2016 12:04:32 +1100 Subject: [PATCH 090/215] Trigger cache refresh when producer property changed or destroyed --- app/models/producer_property.rb | 15 +++++++++++++++ lib/open_food_network/products_cache.rb | 8 ++++++++ spec/models/producer_property_spec.rb | 23 +++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 spec/models/producer_property_spec.rb diff --git a/app/models/producer_property.rb b/app/models/producer_property.rb index 178e7646ff..6e31a2b5f8 100644 --- a/app/models/producer_property.rb +++ b/app/models/producer_property.rb @@ -3,6 +3,9 @@ class ProducerProperty < ActiveRecord::Base default_scope order("#{self.table_name}.position") + after_save :refresh_products_cache + after_destroy :refresh_products_cache_from_destroy + def property_name property.name if property @@ -14,4 +17,16 @@ class ProducerProperty < ActiveRecord::Base Spree::Property.create(name: name, presentation: name) end end + + + private + + def refresh_products_cache + OpenFoodNetwork::ProductsCache.producer_property_changed self + end + + def refresh_products_cache_from_destroy + OpenFoodNetwork::ProductsCache.producer_property_destroyed self + end + end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index d1ce117734..836555a532 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -41,6 +41,14 @@ module OpenFoodNetwork end + def self.producer_property_changed(producer_property) + end + + + def self.producer_property_destroyed(producer_property) + end + + def self.order_cycle_changed(order_cycle) if order_cycle.dated? && !order_cycle.closed? order_cycle.exchanges.outgoing.each do |exchange| diff --git a/spec/models/producer_property_spec.rb b/spec/models/producer_property_spec.rb new file mode 100644 index 0000000000..eaa7f0de0c --- /dev/null +++ b/spec/models/producer_property_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe ProducerProperty do + describe "products caching" do + let(:producer) { create(:supplier_enterprise) } + let(:pp) { producer.producer_properties.first } + + before do + producer.set_producer_property 'Organic Certified', 'NASAA 54321' + end + + it "refreshes the products cache on change" do + expect(OpenFoodNetwork::ProductsCache).to receive(:producer_property_changed).with(pp) + pp.value = 123 + pp.save + end + + it "refreshes the products cache on destruction" do + expect(OpenFoodNetwork::ProductsCache).to receive(:producer_property_destroyed).with(pp) + pp.destroy + end + end +end From 687fb6f0aa060d0bc830a5e05fff143b331f954c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 5 Feb 2016 14:57:31 +1100 Subject: [PATCH 091/215] Enqueue RefreshProductsCacheJob with lower than default priority --- lib/open_food_network/products_cache_refreshment.rb | 2 +- .../open_food_network/products_cache_refreshment_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/products_cache_refreshment.rb b/lib/open_food_network/products_cache_refreshment.rb index 68ed5365c1..276734570f 100644 --- a/lib/open_food_network/products_cache_refreshment.rb +++ b/lib/open_food_network/products_cache_refreshment.rb @@ -41,7 +41,7 @@ module OpenFoodNetwork end def self.enqueue_job(distributor, order_cycle) - Delayed::Job.enqueue RefreshProductsCacheJob.new distributor.id, order_cycle.id + Delayed::Job.enqueue RefreshProductsCacheJob.new(distributor.id, order_cycle.id), priority: 10 end end end diff --git a/spec/lib/open_food_network/products_cache_refreshment_spec.rb b/spec/lib/open_food_network/products_cache_refreshment_spec.rb index bb033cd041..f65b81346e 100644 --- a/spec/lib/open_food_network/products_cache_refreshment_spec.rb +++ b/spec/lib/open_food_network/products_cache_refreshment_spec.rb @@ -13,6 +13,12 @@ module OpenFoodNetwork ProductsCacheRefreshment.refresh distributor, order_cycle end.to enqueue_job RefreshProductsCacheJob end + + it "enqueues the job with a lower than default priority" do + ProductsCacheRefreshment.refresh distributor, order_cycle + job = Delayed::Job.last + expect(job.priority).to be > Delayed::Worker.default_priority + end end describe "when there is an enqueued task, and it is running" do From 1b62dd06b80bd72fa264caa91b3dd960156e872e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 5 Feb 2016 15:16:12 +1100 Subject: [PATCH 092/215] Add products cache integrity checker --- Gemfile | 1 + Gemfile.lock | 2 + .../products_cache_integrity_checker_job.rb | 38 +++++++++++++++++++ config/schedule.rb | 4 ++ lib/tasks/cache.rake | 16 ++++++++ ...oducts_cache_integrity_checker_job_spec.rb | 26 +++++++++++++ 6 files changed, 87 insertions(+) create mode 100644 app/jobs/products_cache_integrity_checker_job.rb create mode 100644 lib/tasks/cache.rake create mode 100644 spec/jobs/products_cache_integrity_checker_job_spec.rb diff --git a/Gemfile b/Gemfile index 300b17ffa3..3690b194b8 100644 --- a/Gemfile +++ b/Gemfile @@ -55,6 +55,7 @@ gem 'figaro' gem 'blockenspiel' gem 'acts-as-taggable-on', '~> 3.4' gem 'paper_trail', '~> 3.0.8' +gem 'diffy' gem 'wicked_pdf' gem 'wkhtmltopdf-binary' diff --git a/Gemfile.lock b/Gemfile.lock index 75763fb4a0..fc93330817 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -248,6 +248,7 @@ GEM devise-encryptable (0.1.2) devise (>= 2.1.0) diff-lcs (1.2.4) + diffy (3.1.0) em-websocket (0.5.0) eventmachine (>= 0.12.9) http_parser.rb (~> 0.5.3) @@ -668,6 +669,7 @@ DEPENDENCIES debugger-linecache deface! delayed_job_active_record + diffy factory_girl_rails figaro foreigner diff --git a/app/jobs/products_cache_integrity_checker_job.rb b/app/jobs/products_cache_integrity_checker_job.rb new file mode 100644 index 0000000000..7bcf86baeb --- /dev/null +++ b/app/jobs/products_cache_integrity_checker_job.rb @@ -0,0 +1,38 @@ +require 'open_food_network/products_renderer' + +ProductsCacheIntegrityCheckerJob = Struct.new(:distributor_id, :order_cycle_id) do + def perform + if diff.any? + Bugsnag.notify RuntimeError.new("Products JSON differs from cached version"), diff: diff.to_s(:color) + end + end + + + private + + def diff + @diff ||= Diffy::Diff.new pretty(cached_json), pretty(rendered_json) + end + + def pretty(json) + JSON.pretty_generate JSON.parse json + end + + def cached_json + Rails.cache.read("products-json-#{distributor_id}-#{order_cycle_id}") || {}.to_json + end + + def rendered_json + OpenFoodNetwork::ProductsRenderer.new(distributor, order_cycle).products_json + rescue OpenFoodNetwork::ProductsRenderer::NoProducts + nil + end + + def distributor + Enterprise.find distributor_id + end + + def order_cycle + OrderCycle.find order_cycle_id + end +end diff --git a/config/schedule.rb b/config/schedule.rb index a09ca55dce..1846a6358d 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -7,6 +7,10 @@ env "MAILTO", "rohan@rohanmitchell.com" # If we use -e with a file containing specs, rspec interprets it and filters out our examples job_type :run_file, "cd :path; :environment_variable=:environment bundle exec script/rails runner :task :output" +every 1.hour do + rake 'openfoodnetwork:cache:check_products_cache_integrity' +end + every 1.day, at: '12:05am' do run_file "lib/open_food_network/integrity_checker.rb" end diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake new file mode 100644 index 0000000000..2eefb8a49f --- /dev/null +++ b/lib/tasks/cache.rake @@ -0,0 +1,16 @@ +namespace :openfoodnetwork do + namespace :cache do + desc 'check the integrity of the products cache' + task :check_products_cache_integrity => :environment do + exchanges = Exchange. + outgoing. + joins(:order_cycle). + merge(OrderCycle.dated). + merge(OrderCycle.not_closed) + + exchanges.each do |exchange| + Delayed::Job.enqueue ProductsCacheIntegrityCheckerJob.new(exchange.receiver, exchange.order_cycle), priority: 20 + end + end + end +end diff --git a/spec/jobs/products_cache_integrity_checker_job_spec.rb b/spec/jobs/products_cache_integrity_checker_job_spec.rb new file mode 100644 index 0000000000..1770f9bf84 --- /dev/null +++ b/spec/jobs/products_cache_integrity_checker_job_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' +require 'open_food_network/products_renderer' + +describe ProductsCacheIntegrityCheckerJob do + describe "reporting on differences between the products cache and the current products" do + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle) } + let(:job) { ProductsCacheIntegrityCheckerJob.new distributor.id, order_cycle.id } + + before do + Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", "[1, 2, 3]\n" + OpenFoodNetwork::ProductsRenderer.stub(:new) { double(:pr, products_json: "[1, 3]\n") } + end + + it "reports errors" do + expect(Bugsnag).to receive(:notify) + run_job job + end + + it "deals with nil cached_json" do + Rails.cache.clear + expect(Bugsnag).to receive(:notify) + run_job job + end + end +end From 71862e00a7c49c21873f4d4fcb2149d3d6eef0d9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 24 Feb 2016 16:11:59 +1100 Subject: [PATCH 093/215] Perform products cache refresh when producer property changed or destroyed --- app/models/producer_property.rb | 1 + lib/open_food_network/products_cache.rb | 9 ++++ .../open_food_network/products_cache_spec.rb | 42 +++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/app/models/producer_property.rb b/app/models/producer_property.rb index 6e31a2b5f8..5b9fdd6d1c 100644 --- a/app/models/producer_property.rb +++ b/app/models/producer_property.rb @@ -1,4 +1,5 @@ class ProducerProperty < ActiveRecord::Base + belongs_to :producer, class_name: 'Enterprise' belongs_to :property, class_name: 'Spree::Property' default_scope order("#{self.table_name}.position") diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index 836555a532..bdf9ee79b1 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -42,10 +42,19 @@ module OpenFoodNetwork def self.producer_property_changed(producer_property) + products = producer_property.producer.supplied_products + variants = Spree::Variant. + where(is_master: false, deleted_at: nil). + where(product_id: products) + + exchanges_featuring_variants(variants).each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end end def self.producer_property_destroyed(producer_property) + producer_property_changed producer_property end diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index 6c5d3019d3..7db47543ad 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -118,6 +118,48 @@ module OpenFoodNetwork end + describe "when a producer property is changed" do + let(:s) { create(:supplier_enterprise) } + let(:pp) { s.producer_properties.last } + let(:product) { create(:simple_product, supplier: s) } + let(:v1) { create(:variant, product: product) } + let(:v2) { create(:variant, product: product) } + let(:v_deleted) { create(:variant, product: product, deleted_at: Time.now) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let(:d3) { create(:distributor_enterprise) } + let(:oc) { create(:open_order_cycle) } + let!(:ex1) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d1, variants: [v1]) } + let!(:ex2) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d2, variants: [v1, v2]) } + let!(:ex3) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d3, variants: [product.master, v_deleted]) } + + before do + s.set_producer_property :organic, 'NASAA 12345' + end + + it "refreshes the distributions the supplied variants appear in" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc).once + ProductsCache.producer_property_changed pp + end + + it "doesn't respond to master or deleted variants" do + expect(ProductsCache).to receive(:refresh_cache).with(d3, oc).never + ProductsCache.producer_property_changed pp + end + end + + + describe "when a producer property is destroyed" do + let(:producer_property) { double(:producer_property) } + + it "triggers the same update as a change to the producer property" do + expect(ProductsCache).to receive(:producer_property_changed).with(producer_property) + ProductsCache.producer_property_destroyed producer_property + end + end + + describe "when an order cycle is changed" do let(:variant) { create(:variant) } let(:s) { create(:supplier_enterprise) } From c07fefde1aaa07c73036e5ad429d8ca9264675c6 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Wed, 24 Feb 2016 20:23:13 +0000 Subject: [PATCH 094/215] Change non-js translations to be done in rails --- app/views/spree/users/_fat.html.haml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index e1d1d147b8..c31a16c9ec 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -2,13 +2,13 @@ .columns.small-12.fat %table %tr - %th.order1{"bo-text" => "'transaction' | t"} - %th.order2{"bo-text" => "'transaction_date' | t"} - %th.order3.show-for-large-up{"bo-text" => "'payment_state' | t"} - %th.order4.show-for-large-up{"bo-text" => "'shipping_state' | t"} - %th.order5.text-right{"bo-text" => "'value' | t"} - %th.order6.text-right.show-for-large-up{"bo-text" => "'outstanding_balance' | t"} - %th.order7.text-right{"bo-text" => "'running_balance' | t"} + %th.order1= t :transaction + %th.order2= t :transaction_date + %th.order3.show-for-large-up= t :payment_state + %th.order4.show-for-large-up= t :shipping_state + %th.order5.text-right= t :value + %th.order6.text-right.show-for-large-up= t :outstanding_balance + %th.order7.text-right= t :running_balance %tbody.transaction-group{"ng-repeat" => "order in distributor.distributed_orders", "ng-class-odd"=>"'odd'", "ng-class-even"=>"'even'"} %tr.order-row %td.order1 @@ -20,7 +20,7 @@ %td.order6.text-right.show-for-large-up{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} %tr.payment-row{"ng-repeat" => "payment in order.payments"} - %td.order1{"bo-text" => "('payment' | t)"} + %td.order1= t :payment %td.order2{"bo-text" => "payment.updated_at"} %td.order3.show-for-large-up{"bo-text" => "payment.payment_method"} %td.order4.show-for-large-up From 8e88cd6255e389072fac45a89e9b7d443e84cc2f Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Wed, 24 Feb 2016 20:33:37 +0000 Subject: [PATCH 095/215] Localise dates --- app/serializers/api/order_serializer.rb | 2 +- app/serializers/api/payment_serializer.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index 162c905e2b..7105e3577b 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -4,7 +4,7 @@ class Api::OrderSerializer < ActiveModel::Serializer has_many :payments, serializer: Api::PaymentSerializer def completed_at - object.completed_at.blank? ? "" : object.completed_at.to_formatted_s(:long_ordinal) + object.completed_at.blank? ? "" : I18n.l(object.completed_at, format: :long) #.to_formatted_s(:long_ordinal) end def total diff --git a/app/serializers/api/payment_serializer.rb b/app/serializers/api/payment_serializer.rb index 019e140ff3..505a5a79e1 100644 --- a/app/serializers/api/payment_serializer.rb +++ b/app/serializers/api/payment_serializer.rb @@ -9,6 +9,6 @@ class Api::PaymentSerializer < ActiveModel::Serializer end def updated_at - object.updated_at.to_formatted_s(:long_ordinal) + I18n.l(object.updated_at, format: :long) end end From b84f49a1c37a0874859ce57439b2efa69c9d296d Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Wed, 24 Feb 2016 20:39:26 +0000 Subject: [PATCH 096/215] Satisfy style police --- app/helpers/injection_helper.rb | 8 +++- app/models/spree/user_decorator.rb | 2 +- app/serializers/api/order_serializer.rb | 42 ++++++++++--------- .../api/orders_by_distributor_serializer.rb | 29 ++++++------- app/serializers/api/payment_serializer.rb | 22 +++++----- 5 files changed, 56 insertions(+), 47 deletions(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 6c35bc775f..aa5148b394 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -54,8 +54,12 @@ module InjectionHelper def inject_orders_by_distributor # Convert ActiveRecord::Relation to array for serialization data_array = spree_current_user.orders_by_distributor.to_a - data_array.each{|enterprise| enterprise.distributed_orders.each{|order| order.payments.keep_if{|payment| payment.state == "completed"} }} - data_array.sort!{|a,b| b.distributed_orders.length <=> a.distributed_orders.length} + data_array.each do |enterprise| + enterprise.distributed_orders.each do |order| + order.payments.keep_if{ |payment| payment.state == "completed" } + end + end + data_array.sort!{ |a, b| b.distributed_orders.length <=> a.distributed_orders.length } inject_json_ams "orders_by_distributor", data_array, Api::OrdersByDistributorSerializer end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 2578f417cc..6c5027a228 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -56,7 +56,7 @@ Spree.user_class.class_eval do # Returns orders and their associated payments for all distributors that have been ordered from def orders_by_distributor - Enterprise.includes(distributed_orders: :payments).where(enterprises: {id: self.enterprises_ordered_from }, spree_orders: {state: 'complete', user_id: self.id}).order('spree_orders.completed_at DESC') + Enterprise.includes(distributed_orders: :payments).where(enterprises: { id: self.enterprises_ordered_from }, spree_orders: { state: 'complete', user_id: self.id }).order('spree_orders.completed_at DESC') end private diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index 7105e3577b..9d7a453b18 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -1,29 +1,31 @@ -class Api::OrderSerializer < ActiveModel::Serializer - attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :payments, :path +module Api + class OrderSerializer < ActiveModel::Serializer + attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state, :outstanding_balance, :payments, :path - has_many :payments, serializer: Api::PaymentSerializer + has_many :payments, serializer: Api::PaymentSerializer - def completed_at - object.completed_at.blank? ? "" : I18n.l(object.completed_at, format: :long) #.to_formatted_s(:long_ordinal) - end + def completed_at + object.completed_at.blank? ? "" : I18n.l(object.completed_at, format: :long) #.to_formatted_s(:long_ordinal) + end - def total - object.total.to_money.to_s - end + def total + object.total.to_money.to_s + end - def shipment_state - object.shipment_state ? object.shipment_state.humanize : nil # Or a call to t() here? - end + def shipment_state + object.shipment_state ? object.shipment_state.humanize : nil # Or a call to t() here? + end - def payment_state - object.payment_state ? object.payment_state.humanize : nil # Or a call to t() here? - end + def payment_state + object.payment_state ? object.payment_state.humanize : nil # Or a call to t() here? + end - def state - object.state ? object.state.humanize : nil # Or a call to t() here? - end + def state + object.state ? object.state.humanize : nil # Or a call to t() here? + end - def path - Spree::Core::Engine.routes_url_helpers.order_url(object.number, only_path: true) + def path + Spree::Core::Engine.routes_url_helpers.order_url(object.number, only_path: true) + end end end diff --git a/app/serializers/api/orders_by_distributor_serializer.rb b/app/serializers/api/orders_by_distributor_serializer.rb index 3d04bf2a03..b920272df2 100644 --- a/app/serializers/api/orders_by_distributor_serializer.rb +++ b/app/serializers/api/orders_by_distributor_serializer.rb @@ -1,17 +1,18 @@ -class Api::OrdersByDistributorSerializer < ActiveModel::Serializer - attributes :name, :id, :hash, :balance, :logo, :distributed_orders - has_many :distributed_orders, serializer: Api::OrderSerializer +module Api + class OrdersByDistributorSerializer < ActiveModel::Serializer + attributes :name, :id, :hash, :balance, :logo, :distributed_orders + has_many :distributed_orders, serializer: Api::OrderSerializer - def balance - object.distributed_orders.map(&:outstanding_balance).reduce(:+).to_money.to_s + def balance + object.distributed_orders.map(&:outstanding_balance).reduce(:+).to_money.to_s + end + + def hash + object.to_param + end + + def logo + object.logo(:small) if object.logo? + end end - - def hash - object.to_param - end - - def logo - object.logo(:small) if object.logo? - end - end diff --git a/app/serializers/api/payment_serializer.rb b/app/serializers/api/payment_serializer.rb index 505a5a79e1..d48f75a07f 100644 --- a/app/serializers/api/payment_serializer.rb +++ b/app/serializers/api/payment_serializer.rb @@ -1,14 +1,16 @@ -class Api::PaymentSerializer < ActiveModel::Serializer - attributes :amount, :updated_at, :payment_method - def payment_method - object.payment_method.name - end +module Api + class PaymentSerializer < ActiveModel::Serializer + attributes :amount, :updated_at, :payment_method + def payment_method + object.payment_method.name + end - def amount - object.amount.to_money.to_s - end + def amount + object.amount.to_money.to_s + end - def updated_at - I18n.l(object.updated_at, format: :long) + def updated_at + I18n.l(object.updated_at, format: :long) + end end end From b5204a48206f50017a9fc81ae04065ee0dbfbcd5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 25 Feb 2016 11:08:53 +1100 Subject: [PATCH 097/215] Refresh cache when option value changed or destroyed --- app/models/spree/option_value_decorator.rb | 20 +++++++++++++++++ spec/models/spree/option_value_spec.rb | 26 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 app/models/spree/option_value_decorator.rb create mode 100644 spec/models/spree/option_value_spec.rb diff --git a/app/models/spree/option_value_decorator.rb b/app/models/spree/option_value_decorator.rb new file mode 100644 index 0000000000..cfe9c23cca --- /dev/null +++ b/app/models/spree/option_value_decorator.rb @@ -0,0 +1,20 @@ +module Spree + OptionValue.class_eval do + after_save :refresh_products_cache + around_destroy :refresh_products_cache_from_destroy + + + private + + def refresh_products_cache + variants(:reload).each &:refresh_products_cache + end + + def refresh_products_cache_from_destroy + vs = variants(:reload).to_a + yield + vs.each &:refresh_products_cache + end + + end +end diff --git a/spec/models/spree/option_value_spec.rb b/spec/models/spree/option_value_spec.rb new file mode 100644 index 0000000000..f784e08af2 --- /dev/null +++ b/spec/models/spree/option_value_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +module Spree + describe OptionValue do + describe "products cache" do + let(:variant) { create(:variant) } + let(:option_value) { create(:option_value) } + + before do + variant.option_values << option_value + option_value.reload + end + + it "refreshes the products cache on change, via variant" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).with(variant) + option_value.name = 'foo' + option_value.save! + end + + it "refreshes the products cache on destruction, via variant" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).with(variant) + option_value.destroy + end + end + end +end From 5e71790cd1d788b1f2afac26426c5e5c1d18cf39 Mon Sep 17 00:00:00 2001 From: Nicolas Blanc Date: Thu, 25 Feb 2016 11:04:21 +0000 Subject: [PATCH 098/215] new validation5 02/25/2016 --- app/views/admin/order_cycles/index.html.haml | 2 +- app/views/spree/admin/orders/bulk_management.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index de166aa4fc..05e6a8bd38 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -44,4 +44,4 @@ = f.fields_for :collection do |order_cycle_form| = render 'admin/order_cycles/row', order_cycle_form: order_cycle_form - = f.submit t(:update) + = f.submit t :update \ No newline at end of file diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index d11f8dffc9..51b13596dd 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -151,7 +151,7 @@ = t "products_price" %th.actions %th.actions - = t "ask"  + = t("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}}" } From 8928e461d4f166bf22cc95c68c58ec0e3d586167 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 26 Feb 2016 09:59:16 +1100 Subject: [PATCH 099/215] Refresh cache when option type changed --- app/models/spree/option_type_decorator.rb | 13 +++++++++++++ spec/models/spree/option_type_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 app/models/spree/option_type_decorator.rb create mode 100644 spec/models/spree/option_type_spec.rb diff --git a/app/models/spree/option_type_decorator.rb b/app/models/spree/option_type_decorator.rb new file mode 100644 index 0000000000..aea706c847 --- /dev/null +++ b/app/models/spree/option_type_decorator.rb @@ -0,0 +1,13 @@ +module Spree + OptionType.class_eval do + has_many :products, through: :product_option_types + after_save :refresh_products_cache + + + private + + def refresh_products_cache + products(:reload).each &:refresh_products_cache + end + end +end diff --git a/spec/models/spree/option_type_spec.rb b/spec/models/spree/option_type_spec.rb new file mode 100644 index 0000000000..e93f35ec49 --- /dev/null +++ b/spec/models/spree/option_type_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +module Spree + describe OptionType do + describe "products cache" do + let!(:product) { create(:simple_product, option_types: [option_type]) } + let(:option_type) { create(:option_type) } + + before { option_type.reload } + + it "refreshes the products cache on change, via product" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) + option_type.name = 'foo' + option_type.save! + end + + # When a OptionType is destroyed, the destruction of the OptionValues refreshes the cache + end + end +end From 45a7b13e9af79ab8cd608a372a47ce20c5a3b904 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 26 Feb 2016 10:09:16 +1100 Subject: [PATCH 100/215] Refresh cache when option type destroyed --- spec/models/spree/option_type_spec.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/spec/models/spree/option_type_spec.rb b/spec/models/spree/option_type_spec.rb index e93f35ec49..cfc0654733 100644 --- a/spec/models/spree/option_type_spec.rb +++ b/spec/models/spree/option_type_spec.rb @@ -4,9 +4,14 @@ module Spree describe OptionType do describe "products cache" do let!(:product) { create(:simple_product, option_types: [option_type]) } + let(:variant) { product.variants.first } let(:option_type) { create(:option_type) } + let(:option_value) { create(:option_value, option_type: option_type) } - before { option_type.reload } + before do + option_type.reload + variant.option_values << option_value + end it "refreshes the products cache on change, via product" do expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(product) @@ -14,7 +19,10 @@ module Spree option_type.save! end - # When a OptionType is destroyed, the destruction of the OptionValues refreshes the cache + it "refreshes the products cache on destruction, via option value destruction" do + expect(OpenFoodNetwork::ProductsCache).to receive(:variant_changed).with(variant) + option_type.destroy + end end end end From d89e9620ac153db28e204d231b8aa9903b0eb03d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 26 Feb 2016 12:05:49 +1100 Subject: [PATCH 101/215] Fix output of cache integrity checker errors --- app/jobs/products_cache_integrity_checker_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/products_cache_integrity_checker_job.rb b/app/jobs/products_cache_integrity_checker_job.rb index 7bcf86baeb..89f8a5d7f4 100644 --- a/app/jobs/products_cache_integrity_checker_job.rb +++ b/app/jobs/products_cache_integrity_checker_job.rb @@ -3,7 +3,7 @@ require 'open_food_network/products_renderer' ProductsCacheIntegrityCheckerJob = Struct.new(:distributor_id, :order_cycle_id) do def perform if diff.any? - Bugsnag.notify RuntimeError.new("Products JSON differs from cached version"), diff: diff.to_s(:color) + Bugsnag.notify RuntimeError.new("Products JSON differs from cached version for distributor: #{@distributor_id}, order cycle: #{@order_cycle_id}"), diff: diff.to_s(:text) end end From 21ce7ab30a3dc309cc0dd34174b8cf48395796e6 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 26 Feb 2016 13:04:55 +1100 Subject: [PATCH 102/215] Fix integrity checker error message, add task to warm products cache --- .../products_cache_integrity_checker_job.rb | 2 +- lib/tasks/cache.rake | 31 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/jobs/products_cache_integrity_checker_job.rb b/app/jobs/products_cache_integrity_checker_job.rb index 89f8a5d7f4..5702380309 100644 --- a/app/jobs/products_cache_integrity_checker_job.rb +++ b/app/jobs/products_cache_integrity_checker_job.rb @@ -3,7 +3,7 @@ require 'open_food_network/products_renderer' ProductsCacheIntegrityCheckerJob = Struct.new(:distributor_id, :order_cycle_id) do def perform if diff.any? - Bugsnag.notify RuntimeError.new("Products JSON differs from cached version for distributor: #{@distributor_id}, order cycle: #{@order_cycle_id}"), diff: diff.to_s(:text) + Bugsnag.notify RuntimeError.new("Products JSON differs from cached version for distributor: #{distributor_id}, order cycle: #{order_cycle_id}"), diff: diff.to_s(:text) end end diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index 2eefb8a49f..7b20a69c6a 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -1,16 +1,29 @@ namespace :openfoodnetwork do namespace :cache do desc 'check the integrity of the products cache' - task :check_products_cache_integrity => :environment do - exchanges = Exchange. - outgoing. - joins(:order_cycle). - merge(OrderCycle.dated). - merge(OrderCycle.not_closed) - - exchanges.each do |exchange| - Delayed::Job.enqueue ProductsCacheIntegrityCheckerJob.new(exchange.receiver, exchange.order_cycle), priority: 20 + task :check_products_integrity => :environment do + active_exchanges.each do |exchange| + Delayed::Job.enqueue ProductsCacheIntegrityCheckerJob.new(exchange.receiver_id, exchange.order_cycle_id), priority: 20 end end + + + desc 'warm the products cache' + task :warm_products => :environment do + active_exchanges.each do |exchange| + Delayed::Job.enqueue RefreshProductsCacheJob.new(exchange.receiver_id, exchange.order_cycle_id), priority: 10 + end + end + + + private + + def active_exchanges + Exchange. + outgoing. + joins(:order_cycle). + merge(OrderCycle.dated). + merge(OrderCycle.not_closed) + end end end From a8d7732a39661afe7170b9fa4031c719be8a4d80 Mon Sep 17 00:00:00 2001 From: Nicolas Blanc Date: Sun, 28 Feb 2016 19:23:26 +0000 Subject: [PATCH 103/215] elf-pavlik@ee52f1d --- app/views/admin/order_cycles/edit.html.haml | 2 +- app/views/admin/order_cycles/index.html.haml | 10 +++--- .../admin/orders/bulk_management.html.haml | 31 ++++++++++++------- config/locales/en.yml | 11 +++---- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index fe0892c300..a00935f07d 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -5,7 +5,7 @@ %h1 - = t:edit_order_cycle + = t :edit_order_cycle - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index d7a6745b19..d0058618a2 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -30,11 +30,11 @@ %tr %th =t :name %th =t :open - %th =t :close + %th =t :close - unless order_cycles_simple_index - %th =t :supplier - %th =t :coordinator - %th =t :distributors + %th =t :supplier + %th =t :coordinator + %th =t :distributors %th =t :products %th.actions %th.actions @@ -44,4 +44,4 @@ = f.fields_for :collection do |order_cycle_form| = render 'admin/order_cycles/row', order_cycle_form: order_cycle_form - = f.submit t :update + = f.submit t :update \ No newline at end of file diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index a63f173662..891a9decaf 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -4,7 +4,7 @@ - content_for :page_title do %h1.page-title = t "bulk_order_management" - %a{ 'ofn-with-tip' => "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." } What's this? + %a{ 'ofn-with-tip' => "#t('bom_tip')" } = t "admin_entreprise_groups_what_s_this" %h1.page-title Bulk Order Management @@ -30,7 +30,7 @@ %br %select{ :class => "three columns alpha", :id => 'supplier_filter', 'select2-min-search' => 5, 'ng-model' => 'supplierFilter', 'ng-options' => 's.id as s.name for s in suppliers' } .filter_select{ :class => "three columns" } - %label{ :for => 'distributor_filter' }Hub + %label{ :for => 'distributor_filter' } %br %select{ :class => "three columns alpha", :id => 'distributor_filter', 'select2-min-search' => 5, 'ng-model' => 'distributorFilter', 'ng-options' => 'd.id as d.name for d in distributors'} .filter_select{ :class => "three columns" } @@ -49,7 +49,7 @@ %div.shared_resource{ :class => "four columns alpha" } %span{ :class => 'three columns alpha' } %input{ type: 'checkbox', :id => 'shared_resource', 'ng-model' => 'sharedResource'} - Shared Resource? + = t "bom_shared" %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.full_name }} @@ -103,7 +103,7 @@ %div{ :class => "seven columns" }   %div{ :class => "three columns omega" } %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' } - %span{ :class => 'icon-reorder' }   Columns + %span{ :class => 'icon-reorder' } %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } %div.menu{ 'ng-show' => "expanded" } %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } @@ -111,10 +111,12 @@ %span{ :class => 'two columns omega' } {{column.name }} %div.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' } %img.spinner{ src: "/assets/spinning-circles.svg" } - %h1 LOADING ORDERS + %h1 + =t "bom_loading" %div{ :class => "sixteen columns alpha", 'ng-show' => '!RequestMonitor.loading && filteredLineItems.length == 0'} - %h1#no_results No orders found. + %h1#no_results + = t "bom_no_results" %div{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } %form{ name: 'bulk_order_form' } @@ -136,15 +138,20 @@ %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } = t "phone" %th.date{ 'ng-show' => 'columns.order_date.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } Order Date + %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } + =t "bom_date" %th.producer{ 'ng-show' => 'columns.producer.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'supplier.name'; reverse = !reverse" } Producer + %a{ :href => '', 'ng-click' => "predicate = 'supplier.name'; reverse = !reverse" } + = t "producer" %th.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.order_cycle.name'; reverse = !reverse" } Order Cycle + %a{ :href => '', 'ng-click' => "predicate = 'order.order_cycle.name'; reverse = !reverse" } + = t "bom_cycle" %th.hub{ 'ng-show' => 'columns.hub.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } Hub + %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } + = t "bom_hub" %th.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'units_variant.full_name'; reverse = !reverse" } Product: Unit + %a{ :href => '', 'ng-click' => "predicate = 'units_variant.full_name'; reverse = !reverse" } + = t "bom_variant" %th.quantity{ 'ng-show' => 'columns.quantity.visible' } = t "products_quantity" %th.max{ 'ng-show' => 'columns.max.visible' } @@ -155,7 +162,7 @@ = t "products_price" %th.actions %th.actions - = t("ask")  + = t "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}}" } diff --git a/config/locales/en.yml b/config/locales/en.yml index c867a29e67..25c3621fe2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -245,12 +245,12 @@ en: bom_quantity: "Quantity" bom_max: "Max" bom_hub: "Hub" - bom_variant: "Variant" + bom_variant: "Product: Unit" bom_final_weigth_volume: "Weight/Volume" - bom_price: "Price" bom_actions_delete: "Delete Selected" bom_order_error: "Some errors must be resolved before you can update orders.\nAny fields with red borders contain errors." - + bom_loading: "Loading orders" ++ bom_no_results: "No orders found." unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." products: "Products" @@ -526,7 +526,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_oc_is: "Your order cycle for this order is %{name}." products_oc_error: "Please complete your order from %{link} before shopping in a different order cycle." products_oc_current: "your current order cycle" - products_quantity: Quantity products_max_quantity: Max quantity products_distributor: Distributor products_distributor_info: When you select a distributor for your order, their address and pickup times will be displayed here. @@ -764,7 +763,7 @@ Please follow the instructions there to make your enterprise visible on the Open update_user_invoices: "Update User Invoices" update_user_invoice_explained: "Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night." auto_finalise_invoices: "Auto-finalise invoices monthly on the 2nd at 1:30am" - auto_update_invoices: "Auto-update invoices nightly at 1:00am" + auto_update_invoices: "Auto-update invoices nightly at 1:00am" in_progress: "In Progress" started_at: "Started at" queued: "Queued" @@ -841,7 +840,7 @@ Please follow the instructions there to make your enterprise visible on the Open no_orders_found: "No orders found." order_no: "Order No." weight_volume: "Weight/Volume" - remove_tax: "Remove tax" + remove_tax: "Remove tax" tax_settings: "Tax Settings" products_require_tax_category: "products require tax category" admin_shared_address_1: "Address" From cb33ede9dc18b9ef0be2927bb7ef4cd472cab218 Mon Sep 17 00:00:00 2001 From: Nicolas Blanc Date: Sun, 28 Feb 2016 19:54:44 +0000 Subject: [PATCH 104/215] Revert "elf-pavlik@ee52f1d" This reverts commit a8d7732a39661afe7170b9fa4031c719be8a4d80. --- app/views/admin/order_cycles/edit.html.haml | 2 +- app/views/admin/order_cycles/index.html.haml | 10 +++--- .../admin/orders/bulk_management.html.haml | 31 +++++++------------ config/locales/en.yml | 11 ++++--- 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index a00935f07d..fe0892c300 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -5,7 +5,7 @@ %h1 - = t :edit_order_cycle + = t:edit_order_cycle - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index d0058618a2..d7a6745b19 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -30,11 +30,11 @@ %tr %th =t :name %th =t :open - %th =t :close + %th =t :close - unless order_cycles_simple_index - %th =t :supplier - %th =t :coordinator - %th =t :distributors + %th =t :supplier + %th =t :coordinator + %th =t :distributors %th =t :products %th.actions %th.actions @@ -44,4 +44,4 @@ = f.fields_for :collection do |order_cycle_form| = render 'admin/order_cycles/row', order_cycle_form: order_cycle_form - = f.submit t :update \ No newline at end of file + = f.submit t :update diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 891a9decaf..a63f173662 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -4,7 +4,7 @@ - content_for :page_title do %h1.page-title = t "bulk_order_management" - %a{ 'ofn-with-tip' => "#t('bom_tip')" } + %a{ 'ofn-with-tip' => "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." } What's this? = t "admin_entreprise_groups_what_s_this" %h1.page-title Bulk Order Management @@ -30,7 +30,7 @@ %br %select{ :class => "three columns alpha", :id => 'supplier_filter', 'select2-min-search' => 5, 'ng-model' => 'supplierFilter', 'ng-options' => 's.id as s.name for s in suppliers' } .filter_select{ :class => "three columns" } - %label{ :for => 'distributor_filter' } + %label{ :for => 'distributor_filter' }Hub %br %select{ :class => "three columns alpha", :id => 'distributor_filter', 'select2-min-search' => 5, 'ng-model' => 'distributorFilter', 'ng-options' => 'd.id as d.name for d in distributors'} .filter_select{ :class => "three columns" } @@ -49,7 +49,7 @@ %div.shared_resource{ :class => "four columns alpha" } %span{ :class => 'three columns alpha' } %input{ type: 'checkbox', :id => 'shared_resource', 'ng-model' => 'sharedResource'} - = t "bom_shared" + 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.full_name }} @@ -103,7 +103,7 @@ %div{ :class => "seven columns" }   %div{ :class => "three columns omega" } %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' } - %span{ :class => 'icon-reorder' } + %span{ :class => 'icon-reorder' }   Columns %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } %div.menu{ 'ng-show' => "expanded" } %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } @@ -111,12 +111,10 @@ %span{ :class => 'two columns omega' } {{column.name }} %div.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' } %img.spinner{ src: "/assets/spinning-circles.svg" } - %h1 - =t "bom_loading" + %h1 LOADING ORDERS %div{ :class => "sixteen columns alpha", 'ng-show' => '!RequestMonitor.loading && filteredLineItems.length == 0'} - %h1#no_results - = t "bom_no_results" + %h1#no_results No orders found. %div{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } %form{ name: 'bulk_order_form' } @@ -138,20 +136,15 @@ %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } = t "phone" %th.date{ 'ng-show' => 'columns.order_date.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } - =t "bom_date" + %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" } - = t "producer" + %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" } - = t "bom_cycle" + %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" } - = t "bom_hub" + %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.full_name'; reverse = !reverse" } - = t "bom_variant" + %a{ :href => '', 'ng-click' => "predicate = 'units_variant.full_name'; reverse = !reverse" } Product: Unit %th.quantity{ 'ng-show' => 'columns.quantity.visible' } = t "products_quantity" %th.max{ 'ng-show' => 'columns.max.visible' } @@ -162,7 +155,7 @@ = t "products_price" %th.actions %th.actions - = t "ask" + = t("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}}" } diff --git a/config/locales/en.yml b/config/locales/en.yml index 25c3621fe2..c867a29e67 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -245,12 +245,12 @@ en: bom_quantity: "Quantity" bom_max: "Max" bom_hub: "Hub" - bom_variant: "Product: Unit" + bom_variant: "Variant" bom_final_weigth_volume: "Weight/Volume" + bom_price: "Price" bom_actions_delete: "Delete Selected" bom_order_error: "Some errors must be resolved before you can update orders.\nAny fields with red borders contain errors." - bom_loading: "Loading orders" -+ bom_no_results: "No orders found." + unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." products: "Products" @@ -526,6 +526,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_oc_is: "Your order cycle for this order is %{name}." products_oc_error: "Please complete your order from %{link} before shopping in a different order cycle." products_oc_current: "your current order cycle" + products_quantity: Quantity products_max_quantity: Max quantity products_distributor: Distributor products_distributor_info: When you select a distributor for your order, their address and pickup times will be displayed here. @@ -763,7 +764,7 @@ Please follow the instructions there to make your enterprise visible on the Open update_user_invoices: "Update User Invoices" update_user_invoice_explained: "Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night." auto_finalise_invoices: "Auto-finalise invoices monthly on the 2nd at 1:30am" - auto_update_invoices: "Auto-update invoices nightly at 1:00am" + auto_update_invoices: "Auto-update invoices nightly at 1:00am" in_progress: "In Progress" started_at: "Started at" queued: "Queued" @@ -840,7 +841,7 @@ Please follow the instructions there to make your enterprise visible on the Open no_orders_found: "No orders found." order_no: "Order No." weight_volume: "Weight/Volume" - remove_tax: "Remove tax" + remove_tax: "Remove tax" tax_settings: "Tax Settings" products_require_tax_category: "products require tax category" admin_shared_address_1: "Address" From 68db9b9926266a0e76fc0c5ad7687ef2c5bc7498 Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Thu, 25 Feb 2016 13:10:50 -0600 Subject: [PATCH 105/215] fixing faling tests --- app/views/admin/order_cycles/edit.html.haml | 2 +- app/views/admin/order_cycles/index.html.haml | 8 ++-- .../admin/orders/bulk_management.html.haml | 38 ++++++++++++------- config/locales/en.yml | 17 +++++---- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index fe0892c300..a00935f07d 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -5,7 +5,7 @@ %h1 - = t:edit_order_cycle + = t :edit_order_cycle - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index d7a6745b19..10b9b1e345 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -30,11 +30,11 @@ %tr %th =t :name %th =t :open - %th =t :close + %th =t :close - unless order_cycles_simple_index - %th =t :supplier - %th =t :coordinator - %th =t :distributors + %th =t :supplier + %th =t :coordinator + %th =t :distributors %th =t :products %th.actions %th.actions diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index a63f173662..9e9362681d 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -4,9 +4,10 @@ - content_for :page_title do %h1.page-title = t "bulk_order_management" - %a{ 'ofn-with-tip' => "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." } What's this? + %a{ 'ofn-with-tip' => "#t('bom_tip')" } = t "admin_entreprise_groups_what_s_this" - %h1.page-title Bulk Order Management + %h1.page-title + = t "bom_page_title" = render :partial => 'spree/admin/shared/order_sub_menu' @@ -30,7 +31,8 @@ %br %select{ :class => "three columns alpha", :id => 'supplier_filter', 'select2-min-search' => 5, 'ng-model' => 'supplierFilter', 'ng-options' => 's.id as s.name for s in suppliers' } .filter_select{ :class => "three columns" } - %label{ :for => 'distributor_filter' }Hub + %label{ :for => 'distributor_filter' } + = t "bom_hub" %br %select{ :class => "three columns alpha", :id => 'distributor_filter', 'select2-min-search' => 5, 'ng-model' => 'distributorFilter', 'ng-options' => 'd.id as d.name for d in distributors'} .filter_select{ :class => "three columns" } @@ -49,7 +51,7 @@ %div.shared_resource{ :class => "four columns alpha" } %span{ :class => 'three columns alpha' } %input{ type: 'checkbox', :id => 'shared_resource', 'ng-model' => 'sharedResource'} - Shared Resource? + = t "bom_shared" %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.full_name }} @@ -60,7 +62,7 @@ .row .one.column.alpha   .two.columns - %span.two.columns + %span.two.columns = t "group_buy_unit_size" %span.two.columns {{ formattedValueWithUnitName( selectedUnitsProduct.group_buy_unit_size, selectedUnitsProduct, selectedUnitsVariant ) }} .one.column   @@ -103,7 +105,8 @@ %div{ :class => "seven columns" }   %div{ :class => "three columns omega" } %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' } - %span{ :class => 'icon-reorder' }   Columns + %span{ :class => 'icon-reorder' } + = t "bom_columns" %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } %div.menu{ 'ng-show' => "expanded" } %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } @@ -111,10 +114,12 @@ %span{ :class => 'two columns omega' } {{column.name }} %div.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' } %img.spinner{ src: "/assets/spinning-circles.svg" } - %h1 LOADING ORDERS + %h1 + =t "bom_loading" %div{ :class => "sixteen columns alpha", 'ng-show' => '!RequestMonitor.loading && filteredLineItems.length == 0'} - %h1#no_results No orders found. + %h1#no_results + = t "bom_no_results" %div{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } %form{ name: 'bulk_order_form' } @@ -136,15 +141,20 @@ %a{ :href => '', 'ng-click' => "predicate = 'order.phone'; reverse = !reverse" } = t "phone" %th.date{ 'ng-show' => 'columns.order_date.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } Order Date + %a{ :href => '', 'ng-click' => "predicate = 'order.completed_at'; reverse = !reverse" } + =t "bom_date" %th.producer{ 'ng-show' => 'columns.producer.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'supplier.name'; reverse = !reverse" } Producer + %a{ :href => '', 'ng-click' => "predicate = 'supplier.name'; reverse = !reverse" } + = t "producer" %th.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.order_cycle.name'; reverse = !reverse" } Order Cycle + %a{ :href => '', 'ng-click' => "predicate = 'order.order_cycle.name'; reverse = !reverse" } + = t "bom_cycle" %th.hub{ 'ng-show' => 'columns.hub.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } Hub + %a{ :href => '', 'ng-click' => "predicate = 'order.distributor.name'; reverse = !reverse" } + = t "bom_hub" %th.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '', 'ng-click' => "predicate = 'units_variant.full_name'; reverse = !reverse" } Product: Unit + %a{ :href => '', 'ng-click' => "predicate = 'units_variant.full_name'; reverse = !reverse" } + = t "bom_variant" %th.quantity{ 'ng-show' => 'columns.quantity.visible' } = t "products_quantity" %th.max{ 'ng-show' => 'columns.max.visible' } @@ -155,7 +165,7 @@ = t "products_price" %th.actions %th.actions - = t("ask")  + = t "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}}" } diff --git a/config/locales/en.yml b/config/locales/en.yml index c867a29e67..15a978493b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -239,16 +239,20 @@ en: order_payment_paypal_successful: Your payment via PayPal has been processed successfully. order_hub_info: Hub Info + bom_tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." + bom_shared: "Shared Resource?" + bom_columns: "Columns" + bom_page_title: "Bulk Order Management" bom_no: "Order no." bom_date: "Order date" bom_cycle: "Order cycle" - bom_quantity: "Quantity" bom_max: "Max" bom_hub: "Hub" - bom_variant: "Variant" + bom_variant: "Product: Unit" bom_final_weigth_volume: "Weight/Volume" - bom_price: "Price" bom_actions_delete: "Delete Selected" + bom_loading: "Loading orders" + bom_no_results: "No orders found." bom_order_error: "Some errors must be resolved before you can update orders.\nAny fields with red borders contain errors." unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." @@ -526,7 +530,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_oc_is: "Your order cycle for this order is %{name}." products_oc_error: "Please complete your order from %{link} before shopping in a different order cycle." products_oc_current: "your current order cycle" - products_quantity: Quantity products_max_quantity: Max quantity products_distributor: Distributor products_distributor_info: When you select a distributor for your order, their address and pickup times will be displayed here. @@ -764,7 +767,7 @@ Please follow the instructions there to make your enterprise visible on the Open update_user_invoices: "Update User Invoices" update_user_invoice_explained: "Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night." auto_finalise_invoices: "Auto-finalise invoices monthly on the 2nd at 1:30am" - auto_update_invoices: "Auto-update invoices nightly at 1:00am" + auto_update_invoices: "Auto-update invoices nightly at 1:00am" in_progress: "In Progress" started_at: "Started at" queued: "Queued" @@ -841,7 +844,7 @@ Please follow the instructions there to make your enterprise visible on the Open no_orders_found: "No orders found." order_no: "Order No." weight_volume: "Weight/Volume" - remove_tax: "Remove tax" + remove_tax: "Remove tax" tax_settings: "Tax Settings" products_require_tax_category: "products require tax category" admin_shared_address_1: "Address" @@ -864,7 +867,7 @@ Please follow the instructions there to make your enterprise visible on the Open report_customers_type: "Report Type" report_customers_csv: "Download as csv" report_customers_header: "orders header" - report_producers: "Producers: " + report_producers: "Producers: " report_type: "Report Type: " report_hubs: "Hubs: " report_payment: "Payment Methods: " From 54ecdb670d87da842e69a8f058ccc42d2eb6dc85 Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Fri, 26 Feb 2016 17:17:06 -0600 Subject: [PATCH 106/215] fixed failing i18n test in variant overrides --- .../variant_overrides/_products.html.haml | 24 ++++++++++++------- config/locales/en.yml | 4 +++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index 7b94c05f33..14b0298875 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -10,14 +10,22 @@ %col.inheritance{ width: "5%", ng: { show: 'columns.inheritance.visible' } } %thead %tr{ ng: { controller: "ColumnsCtrl" } } - %th.producer{ ng: { show: 'columns.producer.visible' } } = t(:producer) - %th.product{ ng: { show: 'columns.product.visible' } } = t(:product) - %th.sku{ ng: { show: 'columns.sku.visible' } } SKU - %th.price{ ng: { show: 'columns.price.visible' } } = t(:price) - %th.on_hand{ ng: { show: 'columns.on_hand.visible' } } = t(:on_hand) - %th.on_demand{ ng: { show: 'columns.on_demand.visible' } } On Demand? - %th.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } } Enable Stock Level Reset? - %th.inheritance{ ng: { show: 'columns.inheritance.visible' } } Inherit? + %th.producer{ ng: { show: 'columns.producer.visible' } } + = t(:producer) + %th.product{ ng: { show: 'columns.product.visible' } } + = t(:product) + %th.sku{ ng: { show: 'columns.sku.visible' } } + = t(:products_sku) + %th.price{ ng: { show: 'columns.price.visible' } } + = t(:price) + %th.on_hand{ ng: { show: 'columns.on_hand.visible' } } + = t(:on_hand) + %th.on_demand{ ng: { show: 'columns.on_demand.visible' } } + = t(:products_on_demand) + %th.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } } + = t(:products_stock_level_reset) + %th.inheritance{ ng: { show: 'columns.inheritance.visible' } } + = t(:products_inherit) %tbody{bindonce: true, ng: {repeat: 'product in products | hubPermissions:hubPermissions:hub.id | attrFilter:{producer_id:producerFilter} | filter:query' } } = render 'admin/variant_overrides/products_product' = render 'admin/variant_overrides/products_variants' diff --git a/config/locales/en.yml b/config/locales/en.yml index 15a978493b..e26c543935 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -480,11 +480,13 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_name: "name" products_unit: "unit" products_on_hand: "on hand" - products_on_demand: "on demand" + products_on_demand: "On demand?" products_category: "Category" products_tax_category: "tax category" products_available_on: "Available On" + products_inherit: "Inherit?" products_inherits_properties: "Inherits Properties?" + products_stock_level_reset: "Enable Stock Level Reset?" register_title: Register From f394cf559c337b7cb30d2b496abb9d600108c67d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 2 Mar 2016 08:49:36 +1100 Subject: [PATCH 107/215] Fix integrity checker rake task name --- config/schedule.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/schedule.rb b/config/schedule.rb index 1846a6358d..023382330a 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -8,7 +8,7 @@ env "MAILTO", "rohan@rohanmitchell.com" job_type :run_file, "cd :path; :environment_variable=:environment bundle exec script/rails runner :task :output" every 1.hour do - rake 'openfoodnetwork:cache:check_products_cache_integrity' + rake 'openfoodnetwork:cache:check_products_integrity' end every 1.day, at: '12:05am' do From 2abee3fcddab4fbaab208ff11eae564c02abd6b3 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 2 Mar 2016 11:01:41 +1100 Subject: [PATCH 108/215] Extract integrity checking to lib class --- .../products_cache_integrity_checker_job.rb | 24 +++------- .../products_cache_integrity_checker.rb | 44 +++++++++++++++++++ lib/tasks/cache.rake | 6 +-- 3 files changed, 50 insertions(+), 24 deletions(-) create mode 100644 lib/open_food_network/products_cache_integrity_checker.rb diff --git a/app/jobs/products_cache_integrity_checker_job.rb b/app/jobs/products_cache_integrity_checker_job.rb index 5702380309..78feb3a1b0 100644 --- a/app/jobs/products_cache_integrity_checker_job.rb +++ b/app/jobs/products_cache_integrity_checker_job.rb @@ -1,31 +1,17 @@ -require 'open_food_network/products_renderer' +require 'open_food_network/products_cache_integrity_checker' ProductsCacheIntegrityCheckerJob = Struct.new(:distributor_id, :order_cycle_id) do def perform - if diff.any? - Bugsnag.notify RuntimeError.new("Products JSON differs from cached version for distributor: #{distributor_id}, order cycle: #{order_cycle_id}"), diff: diff.to_s(:text) + unless checker.ok? + Bugsnag.notify RuntimeError.new("Products JSON differs from cached version for distributor: #{distributor_id}, order cycle: #{order_cycle_id}"), diff: checker.diff.to_s(:text) end end private - def diff - @diff ||= Diffy::Diff.new pretty(cached_json), pretty(rendered_json) - end - - def pretty(json) - JSON.pretty_generate JSON.parse json - end - - def cached_json - Rails.cache.read("products-json-#{distributor_id}-#{order_cycle_id}") || {}.to_json - end - - def rendered_json - OpenFoodNetwork::ProductsRenderer.new(distributor, order_cycle).products_json - rescue OpenFoodNetwork::ProductsRenderer::NoProducts - nil + def checker + OpenFoodNetwork::ProductsCacheIntegrityChecker.new(distributor, order_cycle) end def distributor diff --git a/lib/open_food_network/products_cache_integrity_checker.rb b/lib/open_food_network/products_cache_integrity_checker.rb new file mode 100644 index 0000000000..cf8363ee5c --- /dev/null +++ b/lib/open_food_network/products_cache_integrity_checker.rb @@ -0,0 +1,44 @@ +require 'open_food_network/products_renderer' + +module OpenFoodNetwork + class ProductsCacheIntegrityChecker + def initialize(distributor, order_cycle) + @distributor = distributor + @order_cycle = order_cycle + end + + def ok? + diff.none? + end + + def diff + @diff ||= Diffy::Diff.new pretty(cached_json), pretty(rendered_json) + end + + + def self.active_exchanges + Exchange. + outgoing. + joins(:order_cycle). + merge(OrderCycle.dated). + merge(OrderCycle.not_closed) + end + + + private + + def cached_json + Rails.cache.read("products-json-#{@distributor.id}-#{@order_cycle.id}") || {}.to_json + end + + def rendered_json + OpenFoodNetwork::ProductsRenderer.new(@distributor, @order_cycle).products_json + rescue OpenFoodNetwork::ProductsRenderer::NoProducts + nil + end + + def pretty(json) + JSON.pretty_generate JSON.parse json + end + end +end diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index 7b20a69c6a..8a564fcddc 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -19,11 +19,7 @@ namespace :openfoodnetwork do private def active_exchanges - Exchange. - outgoing. - joins(:order_cycle). - merge(OrderCycle.dated). - merge(OrderCycle.not_closed) + ProductsCacheIntegrityChecker.active_exchanges end end end From ec55af5b8af2ece4ba66067faa2914d79d9c74a1 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 2 Mar 2016 11:05:03 +1100 Subject: [PATCH 109/215] Display products cache integrity checker results on cache settings admin page --- .../admin/cache_settings_controller.rb | 14 ++++++ .../add_caching.html.haml.deface | 4 ++ app/views/admin/cache_settings/show.html.haml | 18 ++++++++ config/routes.rb | 2 + spec/features/admin/caching_spec.rb | 43 +++++++++++++++++++ 5 files changed, 81 insertions(+) create mode 100644 app/controllers/admin/cache_settings_controller.rb create mode 100644 app/overrides/spree/admin/shared/_configuration_menu/add_caching.html.haml.deface create mode 100644 app/views/admin/cache_settings/show.html.haml create mode 100644 spec/features/admin/caching_spec.rb diff --git a/app/controllers/admin/cache_settings_controller.rb b/app/controllers/admin/cache_settings_controller.rb new file mode 100644 index 0000000000..ada153a6d5 --- /dev/null +++ b/app/controllers/admin/cache_settings_controller.rb @@ -0,0 +1,14 @@ +require 'open_food_network/products_cache_integrity_checker' + +class Admin::CacheSettingsController < Spree::Admin::BaseController + + def show + active_exchanges = OpenFoodNetwork::ProductsCacheIntegrityChecker.active_exchanges + @results = active_exchanges.map do |exchange| + checker = OpenFoodNetwork::ProductsCacheIntegrityChecker.new(exchange.receiver, exchange.order_cycle) + + {distributor: exchange.receiver, order_cycle: exchange.order_cycle, status: checker.ok?, diff: checker.diff} + end + end + +end diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_caching.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_caching.html.haml.deface new file mode 100644 index 0000000000..1eb416e72a --- /dev/null +++ b/app/overrides/spree/admin/shared/_configuration_menu/add_caching.html.haml.deface @@ -0,0 +1,4 @@ +/ insert_bottom "[data-hook='admin_configurations_sidebar_menu']" + +%li + = link_to 'Caching', main_app.admin_cache_settings_path diff --git a/app/views/admin/cache_settings/show.html.haml b/app/views/admin/cache_settings/show.html.haml new file mode 100644 index 0000000000..79b3e5acaf --- /dev/null +++ b/app/views/admin/cache_settings/show.html.haml @@ -0,0 +1,18 @@ +- content_for :page_title do + = t(:cache_settings) + +%table.index + %thead + %tr + %th Distributor + %th Order Cycle + %th Status + %th Diff + %tbody + - @results.each do |result| + %tr + %td= result[:distributor].name + %td= result[:order_cycle].name + %td= result[:status] ? 'OK' : 'Error' + %td + %pre= result[:diff].to_s(:text) diff --git a/config/routes.rb b/config/routes.rb index 62c4153d14..c44f8395de 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -122,6 +122,8 @@ Openfoodnetwork::Application.routes.draw do resource :business_model_configuration, only: [:edit, :update], controller: 'business_model_configuration' + resource :cache_settings + resource :account, only: [:show], controller: 'account' end diff --git a/spec/features/admin/caching_spec.rb b/spec/features/admin/caching_spec.rb new file mode 100644 index 0000000000..22d372e19b --- /dev/null +++ b/spec/features/admin/caching_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' +require 'open_food_network/products_renderer' + +feature 'Caching' do + include AuthenticationWorkflow + include WebHelper + + before { quick_login_as_admin } + + describe "displaying integrity checker results" do + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:open_order_cycle, distributors: [distributor]) } + + it "displays results when things are good" do + # Given matching data + Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", "[1, 2, 3]\n" + OpenFoodNetwork::ProductsRenderer.stub(:new) { double(:pr, products_json: "[1, 2, 3]\n") } + + # When I visit the cache status page + visit spree.admin_path + click_link 'Configuration' + click_link 'Caching' + + # Then I should see some status information + page.should have_content "OK" + end + + it "displays results when there are errors" do + # Given matching data + Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", "[1, 2, 3]\n" + OpenFoodNetwork::ProductsRenderer.stub(:new) { double(:pr, products_json: "[1, 3]\n") } + + # When I visit the cache status page + visit spree.admin_path + click_link 'Configuration' + click_link 'Caching' + + # Then I should see some status information + page.should have_content "Error" + end + + end +end From 4a7a40425adcf05b2a20f45ceb8f8f4fe1dfe849 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 2 Mar 2016 11:38:42 +1100 Subject: [PATCH 110/215] Fix problems in rake file --- lib/tasks/cache.rake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index 8a564fcddc..4f4acb5a97 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -1,3 +1,5 @@ +require 'open_food_network/products_cache_integrity_checker' + namespace :openfoodnetwork do namespace :cache do desc 'check the integrity of the products cache' @@ -19,7 +21,7 @@ namespace :openfoodnetwork do private def active_exchanges - ProductsCacheIntegrityChecker.active_exchanges + OpenFoodNetwork::ProductsCacheIntegrityChecker.active_exchanges end end end From 6a2319e16d0029e9d3a2cf1866adcd80c8f728e8 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Wed, 2 Mar 2016 19:08:57 +0000 Subject: [PATCH 111/215] Remove producers without lat + long from map --- app/assets/javascripts/darkswarm/services/map.js.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/darkswarm/services/map.js.coffee b/app/assets/javascripts/darkswarm/services/map.js.coffee index 96768e8379..c48cc46ad0 100644 --- a/app/assets/javascripts/darkswarm/services/map.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map.js.coffee @@ -2,6 +2,8 @@ Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter) -> new class OfnMap constructor: -> @enterprises = @enterprise_markers(Enterprises.enterprises) + @enterprises = @enterprises.filter (enterprise) -> + enterprise.latitude || enterprise.longitude # Remove enterprises w/o lat or long enterprise_markers: (enterprises) -> @extend(enterprise) for enterprise in visibleFilter(enterprises) From c1d068aeb927ef7cbaa2c9c187fe121860cc917c Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Wed, 2 Mar 2016 20:07:39 +0000 Subject: [PATCH 112/215] Add/modify karma specs --- .../unit/darkswarm/services/map_spec.js.coffee | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee index 4252000460..65de34e89c 100644 --- a/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee @@ -9,6 +9,16 @@ describe "Hubs service", -> orders_close_at: new Date() type: "hub" visible: true + latitude: 50 + longitude: 50 + } + { + id: 3 + active: false + orders_close_at: new Date() + type: "hub" + visible: true + } ] @@ -24,3 +34,6 @@ describe "Hubs service", -> it "builds MapMarkers from enterprises", -> expect(OfnMap.enterprises[0].id).toBe enterprises[0].id + + it "excludes enterprises without latitude or longitude", -> + expect(OfnMap.enterprises.map (e) -> e.id).not.toContain enterprises[1].id From 7e6d544180d500c39d290ec4f26dabbca399f10a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 3 Mar 2016 09:33:32 +1100 Subject: [PATCH 113/215] Do not serialize product count_on_hand - reduce coupling between variant create and products JSON --- app/serializers/api/product_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/serializers/api/product_serializer.rb b/app/serializers/api/product_serializer.rb index 03a66afe89..aa353173dc 100644 --- a/app/serializers/api/product_serializer.rb +++ b/app/serializers/api/product_serializer.rb @@ -36,7 +36,7 @@ class Api::CachedProductSerializer < ActiveModel::Serializer #delegate :cache_key, to: :object include ActionView::Helpers::SanitizeHelper - attributes :id, :name, :permalink, :count_on_hand + attributes :id, :name, :permalink attributes :on_demand, :group_buy, :notes, :description attributes :properties_with_values From 4966290f87e5e7654663793fbefbbe33141157e3 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Thu, 3 Mar 2016 06:41:59 +0000 Subject: [PATCH 114/215] Check missing lat/long instead of zero --- app/assets/javascripts/darkswarm/services/map.js.coffee | 2 +- spec/javascripts/unit/darkswarm/services/map_spec.js.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/map.js.coffee b/app/assets/javascripts/darkswarm/services/map.js.coffee index c48cc46ad0..f4bad04acc 100644 --- a/app/assets/javascripts/darkswarm/services/map.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map.js.coffee @@ -3,7 +3,7 @@ Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter) -> constructor: -> @enterprises = @enterprise_markers(Enterprises.enterprises) @enterprises = @enterprises.filter (enterprise) -> - enterprise.latitude || enterprise.longitude # Remove enterprises w/o lat or long + enterprise.latitude != null || enterprise.longitude != null # Remove enterprises w/o lat or long enterprise_markers: (enterprises) -> @extend(enterprise) for enterprise in visibleFilter(enterprises) diff --git a/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee index 65de34e89c..6e5fcc320f 100644 --- a/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee @@ -9,8 +9,8 @@ describe "Hubs service", -> orders_close_at: new Date() type: "hub" visible: true - latitude: 50 - longitude: 50 + latitude: 0 + longitude: 0 } { id: 3 From 939356ef263f842f949d42c02caa6454af2b05b0 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Thu, 3 Mar 2016 07:18:49 +0000 Subject: [PATCH 115/215] Update spec with nulls --- spec/javascripts/unit/darkswarm/services/map_spec.js.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee index 6e5fcc320f..669f0ca74d 100644 --- a/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee @@ -18,7 +18,8 @@ describe "Hubs service", -> orders_close_at: new Date() type: "hub" visible: true - + latitude: null + longitude: null } ] From bc2223fb8eb8b1266fea44b0ead8774164b49911 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 4 Mar 2016 10:15:57 +1100 Subject: [PATCH 116/215] Fix intermittent spec fails: currency inconsistencies on CI and retry on VOs --- spec/features/admin/variant_overrides_spec.rb | 2 +- spec/models/spree/shipping_method_spec.rb | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/spec/features/admin/variant_overrides_spec.rb b/spec/features/admin/variant_overrides_spec.rb index fe74d9eb2d..037c3e7cf0 100644 --- a/spec/features/admin/variant_overrides_spec.rb +++ b/spec/features/admin/variant_overrides_spec.rb @@ -318,7 +318,7 @@ feature %q{ select2_select hub.name, from: 'hub_id' end - it "alerts the user to the presence of new products, and allows them to be added or hidden" do + it "alerts the user to the presence of new products, and allows them to be added or hidden", retry: 3 do expect(page).to_not have_selector "table#variant-overrides tr#v_#{variant1.id}" expect(page).to_not have_selector "table#variant-overrides tr#v_#{variant2.id}" diff --git a/spec/models/spree/shipping_method_spec.rb b/spec/models/spree/shipping_method_spec.rb index 0e738470dd..daae9a2c4e 100644 --- a/spec/models/spree/shipping_method_spec.rb +++ b/spec/models/spree/shipping_method_spec.rb @@ -39,19 +39,26 @@ module Spree describe "availability" do let(:sm) { build(:shipping_method) } + before do + sm.calculator.preferences[:currency] = Spree::Config.currency + + end + it "is available to orders that match its distributor" do - o = build(:order, ship_address: build(:address), distributor: sm.distributors.first) + o = build(:order, ship_address: build(:address), + distributor: sm.distributors.first, currency: Spree::Config.currency) sm.should be_available_to_order o end it "is not available to orders that do not match its distributor" do o = build(:order, ship_address: build(:address), - distributor: build(:distributor_enterprise)) + distributor: build(:distributor_enterprise), currency: Spree::Config.currency) sm.should_not be_available_to_order o end it "is available to orders with no shipping address" do - o = build(:order, ship_address: nil, distributor: sm.distributors.first) + o = build(:order, ship_address: nil, + distributor: sm.distributors.first, currency: Spree::Config.currency) sm.should be_available_to_order o end end From 1440544b2dbe779804c468674b487d2560d53e70 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 4 Mar 2016 10:29:26 +1100 Subject: [PATCH 117/215] Use persisted models --- spec/models/spree/shipping_method_spec.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spec/models/spree/shipping_method_spec.rb b/spec/models/spree/shipping_method_spec.rb index daae9a2c4e..135b0b0092 100644 --- a/spec/models/spree/shipping_method_spec.rb +++ b/spec/models/spree/shipping_method_spec.rb @@ -37,27 +37,26 @@ module Spree describe "availability" do - let(:sm) { build(:shipping_method) } + let(:sm) { create(:shipping_method) } before do sm.calculator.preferences[:currency] = Spree::Config.currency - end it "is available to orders that match its distributor" do - o = build(:order, ship_address: build(:address), + o = create(:order, ship_address: create(:address), distributor: sm.distributors.first, currency: Spree::Config.currency) sm.should be_available_to_order o end it "is not available to orders that do not match its distributor" do - o = build(:order, ship_address: build(:address), - distributor: build(:distributor_enterprise), currency: Spree::Config.currency) + o = create(:order, ship_address: create(:address), + distributor: create(:distributor_enterprise), currency: Spree::Config.currency) sm.should_not be_available_to_order o end it "is available to orders with no shipping address" do - o = build(:order, ship_address: nil, + o = create(:order, ship_address: nil, distributor: sm.distributors.first, currency: Spree::Config.currency) sm.should be_available_to_order o end From 73b53e02fc8965e73da8ca6128ec09b0af0cdab9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 4 Mar 2016 10:53:48 +1100 Subject: [PATCH 118/215] Exclude performance specs from CI, which were modifying Spree::Config.currency --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0ba99dc9e1..56d94e5952 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ before_script: script: - 'if [ "$KARMA" = "true" ]; then bundle exec rake karma:run; else echo "Skipping karma run"; fi' #- "KNAPSACK_GENERATE_REPORT=true bundle exec rspec spec" - - "bundle exec rake knapsack:rspec" + - "bundle exec rake 'knapsack:rspec[--tag ~performance]'" after_success: - > From 780ec598d6892ee8bd02e261ac4b836c99491024 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 4 Mar 2016 11:31:18 +1100 Subject: [PATCH 119/215] Use preferred_currency instead of preferences[:currency]. Use constant for currency instead of config var. Conflicts: spec/models/spree/shipping_method_spec.rb --- spec/models/spree/shipping_method_spec.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/models/spree/shipping_method_spec.rb b/spec/models/spree/shipping_method_spec.rb index 135b0b0092..33c44b8b6d 100644 --- a/spec/models/spree/shipping_method_spec.rb +++ b/spec/models/spree/shipping_method_spec.rb @@ -38,26 +38,27 @@ module Spree describe "availability" do let(:sm) { create(:shipping_method) } + let(:currency) { 'AUD' } before do - sm.calculator.preferences[:currency] = Spree::Config.currency + sm.calculator.preferred_currency = currency end it "is available to orders that match its distributor" do o = create(:order, ship_address: create(:address), - distributor: sm.distributors.first, currency: Spree::Config.currency) + distributor: sm.distributors.first, currency: currency) sm.should be_available_to_order o end it "is not available to orders that do not match its distributor" do o = create(:order, ship_address: create(:address), - distributor: create(:distributor_enterprise), currency: Spree::Config.currency) + distributor: create(:distributor_enterprise), currency: currency) sm.should_not be_available_to_order o end it "is available to orders with no shipping address" do o = create(:order, ship_address: nil, - distributor: sm.distributors.first, currency: Spree::Config.currency) + distributor: sm.distributors.first, currency: currency) sm.should be_available_to_order o end end From 27d7b3026bb611c9e8345e9fcfd730c93a293475 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 9 Mar 2016 12:28:09 +1100 Subject: [PATCH 120/215] Move OpenFoodNetwork::ProductsCacheIntegrityChecker.active_exchanges to Exchange model --- app/controllers/admin/cache_settings_controller.rb | 3 +-- app/models/exchange.rb | 6 ++++++ .../products_cache_integrity_checker.rb | 9 --------- lib/tasks/cache.rake | 11 ++--------- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/app/controllers/admin/cache_settings_controller.rb b/app/controllers/admin/cache_settings_controller.rb index ada153a6d5..138bbc7b18 100644 --- a/app/controllers/admin/cache_settings_controller.rb +++ b/app/controllers/admin/cache_settings_controller.rb @@ -3,8 +3,7 @@ require 'open_food_network/products_cache_integrity_checker' class Admin::CacheSettingsController < Spree::Admin::BaseController def show - active_exchanges = OpenFoodNetwork::ProductsCacheIntegrityChecker.active_exchanges - @results = active_exchanges.map do |exchange| + @results = Exchange.cachable.map do |exchange| checker = OpenFoodNetwork::ProductsCacheIntegrityChecker.new(exchange.receiver, exchange.order_cycle) {distributor: exchange.receiver, order_cycle: exchange.order_cycle, status: checker.ok?, diff: checker.diff} diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 94aada7e0e..ad290f6c26 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -34,6 +34,12 @@ class Exchange < ActiveRecord::Base joins('INNER JOIN enterprises AS receiver ON (receiver.id = exchanges.receiver_id)'). order("CASE WHEN exchanges.incoming='t' THEN sender.name ELSE receiver.name END") + # Exchanges on order cycles that are dated and are upcoming or open are cached + scope :cachable, outgoing. + joins(:order_cycle). + merge(OrderCycle.dated). + merge(OrderCycle.not_closed) + scope :managed_by, lambda { |user| if user.has_spree_role?('admin') scoped diff --git a/lib/open_food_network/products_cache_integrity_checker.rb b/lib/open_food_network/products_cache_integrity_checker.rb index cf8363ee5c..8098124feb 100644 --- a/lib/open_food_network/products_cache_integrity_checker.rb +++ b/lib/open_food_network/products_cache_integrity_checker.rb @@ -16,15 +16,6 @@ module OpenFoodNetwork end - def self.active_exchanges - Exchange. - outgoing. - joins(:order_cycle). - merge(OrderCycle.dated). - merge(OrderCycle.not_closed) - end - - private def cached_json diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index 4f4acb5a97..64a5d3909a 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -4,7 +4,7 @@ namespace :openfoodnetwork do namespace :cache do desc 'check the integrity of the products cache' task :check_products_integrity => :environment do - active_exchanges.each do |exchange| + Exchange.cachable.each do |exchange| Delayed::Job.enqueue ProductsCacheIntegrityCheckerJob.new(exchange.receiver_id, exchange.order_cycle_id), priority: 20 end end @@ -12,16 +12,9 @@ namespace :openfoodnetwork do desc 'warm the products cache' task :warm_products => :environment do - active_exchanges.each do |exchange| + Exchange.cachable.each do |exchange| Delayed::Job.enqueue RefreshProductsCacheJob.new(exchange.receiver_id, exchange.order_cycle_id), priority: 10 end end - - - private - - def active_exchanges - OpenFoodNetwork::ProductsCacheIntegrityChecker.active_exchanges - end end end From 6f29a8b64239de51e2930a09cebc11f8186cd4ca Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 9 Mar 2016 13:29:33 +1100 Subject: [PATCH 121/215] Refresh cache when inventory setting product_selection_from_inventory_only is changed --- app/models/spree/preference_decorator.rb | 31 +++++++++++++++++++ lib/open_food_network/products_cache.rb | 7 +++++ spec/features/admin/enterprises_spec.rb | 26 ++++++++++++++++ .../open_food_network/products_cache_spec.rb | 10 ++++++ spec/models/spree/preference_spec.rb | 23 ++++++++++++++ 5 files changed, 97 insertions(+) create mode 100644 app/models/spree/preference_decorator.rb create mode 100644 spec/models/spree/preference_spec.rb diff --git a/app/models/spree/preference_decorator.rb b/app/models/spree/preference_decorator.rb new file mode 100644 index 0000000000..c3ab3f9a94 --- /dev/null +++ b/app/models/spree/preference_decorator.rb @@ -0,0 +1,31 @@ +require 'open_food_network/products_cache' + +module Spree + Preference.class_eval do + after_save :refresh_products_cache + + # When the setting preferred_product_selection_from_inventory_only has changed, we want to + # refresh all active exchanges for this enterprise. + def refresh_products_cache + if product_selection_from_inventory_only_changed? + OpenFoodNetwork::ProductsCache.distributor_changed(enterprise) + end + end + + + private + + def product_selection_from_inventory_only_changed? + key =~ product_selection_from_inventory_only_regex + end + + def enterprise + enterprise_id = key.match(product_selection_from_inventory_only_regex)[1] + enterprise = Enterprise.find enterprise_id + end + + def product_selection_from_inventory_only_regex + /^enterprise\/product_selection_from_inventory_only\/(\d+)$/ + end + end +end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index bdf9ee79b1..d5b39609d9 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -88,6 +88,13 @@ module OpenFoodNetwork end + def self.distributor_changed(enterprise) + Exchange.cachable.where(receiver_id: enterprise).each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end + end + + private def self.exchanges_featuring_variants(variants, distributor: nil) diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index aed96b5a91..4bd58715eb 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -251,6 +251,32 @@ feature %q{ end + describe "inventory settings", js: true do + let!(:enterprise) { create(:distributor_enterprise) } + let!(:product) { create(:simple_product) } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [enterprise], variants: [product.variants.first]) } + + before do + Delayed::Job.destroy_all + quick_login_as_admin + end + + it "refreshes the cache when I change what products appear on my shopfront" do + # Given a product that's not in my inventory, but is in an active order cycle + + # When I change which products appear on the shopfront + visit edit_admin_enterprise_path(enterprise) + within(".side_menu") { click_link 'Inventory Settings' } + choose 'enterprise_preferred_product_selection_from_inventory_only_1' + + # Then a job should have been enqueued to refresh the cache + expect do + click_button 'Update' + end.to enqueue_job RefreshProductsCacheJob, distributor_id: enterprise.id, order_cycle_id: order_cycle.id + end + end + + context "as an Enterprise user", js: true do let(:supplier1) { create(:supplier_enterprise, name: 'First Supplier') } let(:supplier2) { create(:supplier_enterprise, name: 'Another Supplier') } diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index 7db47543ad..6b0bc61eb2 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -376,6 +376,16 @@ module OpenFoodNetwork end end + describe "when a distributor enterprise is changed" do + let(:d) { create(:distributor_enterprise) } + let(:oc) { create(:open_order_cycle, distributors: [d]) } + + it "updates each distribution the enterprise is active in" do + expect(ProductsCache).to receive(:refresh_cache).with(d, oc) + ProductsCache.distributor_changed d + end + end + describe "refreshing the cache" do let(:distributor) { double(:distributor) } let(:order_cycle) { double(:order_cycle) } diff --git a/spec/models/spree/preference_spec.rb b/spec/models/spree/preference_spec.rb new file mode 100644 index 0000000000..ff71c9c9c6 --- /dev/null +++ b/spec/models/spree/preference_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +module Spree + describe Preference do + describe "refreshing the products cache" do + it "reports when product_selection_from_inventory_only has changed" do + p = Preference.new(key: 'enterprise/product_selection_from_inventory_only/123') + expect(p.send(:product_selection_from_inventory_only_changed?)).to be_true + end + + it "reports when product_selection_from_inventory_only has not changed" do + p = Preference.new(key: 'enterprise/shopfront_message/123') + expect(p.send(:product_selection_from_inventory_only_changed?)).to be_false + end + + it "looks up the referenced enterprise" do + e = create(:distributor_enterprise) + p = Preference.new(key: "enterprise/product_selection_from_inventory_only/#{e.id}") + expect(p.send(:enterprise)).to eql e + end + end + end +end From 9645ec727beb359e2e6afdd5e10ce5951918c42c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 9 Mar 2016 14:07:04 +1100 Subject: [PATCH 122/215] Refresh products cache when inventory items are changed --- app/models/inventory_item.rb | 11 +++++++++++ lib/open_food_network/products_cache.rb | 7 +++++++ .../open_food_network/products_cache_spec.rb | 19 +++++++++++++++++++ spec/models/inventory_item_spec.rb | 16 ++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 spec/models/inventory_item_spec.rb diff --git a/app/models/inventory_item.rb b/app/models/inventory_item.rb index 2648e38341..60e1713612 100644 --- a/app/models/inventory_item.rb +++ b/app/models/inventory_item.rb @@ -1,3 +1,5 @@ +require 'open_food_network/products_cache' + class InventoryItem < ActiveRecord::Base attr_accessible :enterprise, :enterprise_id, :variant, :variant_id, :visible @@ -11,4 +13,13 @@ class InventoryItem < ActiveRecord::Base scope :visible, where(visible: true) scope :hidden, where(visible: false) + + after_save :refresh_products_cache + + + private + + def refresh_products_cache + OpenFoodNetwork::ProductsCache.inventory_item_changed self + end end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index d5b39609d9..f6ef15829f 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -95,6 +95,13 @@ module OpenFoodNetwork end + def self.inventory_item_changed(inventory_item) + exchanges_featuring_variants(inventory_item.variant, distributor: inventory_item.enterprise).each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end + end + + private def self.exchanges_featuring_variants(variants, distributor: nil) diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index 6b0bc61eb2..a395873f45 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -386,6 +386,25 @@ module OpenFoodNetwork end end + describe "when an inventory item is changed" do + let!(:d) { create(:distributor_enterprise) } + let!(:v) { create(:variant) } + let!(:oc1) { create(:open_order_cycle, distributors: [d], variants: [v]) } + let(:oc2) { create(:open_order_cycle, distributors: [d], variants: []) } + let!(:ii) { create(:inventory_item, enterprise: d, variant: v) } + + it "updates each distribution for that enterprise+variant" do + expect(ProductsCache).to receive(:refresh_cache).with(d, oc1) + ProductsCache.inventory_item_changed ii + end + + it "doesn't update distributions that don't feature the variant" do + oc2 + expect(ProductsCache).to receive(:refresh_cache).with(d, oc2).never + ProductsCache.inventory_item_changed ii + end + end + describe "refreshing the cache" do let(:distributor) { double(:distributor) } let(:order_cycle) { double(:order_cycle) } diff --git a/spec/models/inventory_item_spec.rb b/spec/models/inventory_item_spec.rb new file mode 100644 index 0000000000..99a16d3fdb --- /dev/null +++ b/spec/models/inventory_item_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' +require 'open_food_network/products_cache' + +describe InventoryItem do + describe "caching" do + let(:ii) { create(:inventory_item) } + + it "refreshes the products cache on save" do + expect(OpenFoodNetwork::ProductsCache).to receive(:inventory_item_changed).with(ii) + ii.visible = false + ii.save + end + + # Inventory items are not destroyed + end +end From 7e65b3176d5aa585cd845dc490ca3a365f773ce6 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 9 Mar 2016 14:20:48 +1100 Subject: [PATCH 123/215] Add retry to failing payment method spec --- spec/features/admin/payment_method_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/admin/payment_method_spec.rb b/spec/features/admin/payment_method_spec.rb index aa7caa2e0b..c208d7dd47 100644 --- a/spec/features/admin/payment_method_spec.rb +++ b/spec/features/admin/payment_method_spec.rb @@ -30,7 +30,7 @@ feature %q{ payment_method.distributors.should == [@distributors[0]] end - scenario "updating a payment method" do + scenario "updating a payment method", retry: 3 do pm = create(:payment_method, distributors: [@distributors[0]]) login_to_admin_section From d925c2aefc51b46ab06fc19b8389a184f9211340 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 2 Mar 2016 16:17:40 +1100 Subject: [PATCH 124/215] Translate Spree payment, shipment and order states - Copied translations for payment_states, shipment_states and order_states into locale en.yml. - Enabled global Javascript function `translate` to deal with scopes like 'spree.shipment_states'. - Removed `humanize` call from order serializer and added translation scopes to accounts page. - Test OrderSerializer for untranslated attributes - Require spec helper in serializer specs --- .../darkswarm/i18n.translate.js.coffee | 9 ++++-- app/serializers/api/order_serializer.rb | 8 ++--- app/views/spree/users/_fat.html.haml | 4 +-- app/views/spree/users/_skinny.html.haml | 1 - config/locales/en.yml | 31 +++++++++++++++++++ .../serializers/enterprise_serializer_spec.rb | 2 +- spec/serializers/order_serializer_spec.rb | 8 ++--- .../orders_by_distributor_serializer_spec.rb | 2 +- 8 files changed, 50 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/darkswarm/i18n.translate.js.coffee b/app/assets/javascripts/darkswarm/i18n.translate.js.coffee index c455b6d9e1..eda092da83 100644 --- a/app/assets/javascripts/darkswarm/i18n.translate.js.coffee +++ b/app/assets/javascripts/darkswarm/i18n.translate.js.coffee @@ -4,8 +4,13 @@ window.translate = (key, options = {}) -> unless 'I18n' of window console.log 'The I18n object is undefined. Cannot translate text.' return key - return key unless key of I18n - text = I18n[key] + dict = I18n + parts = key.split '.' + while (parts.length) + part = parts.shift() + return key unless part of dict + dict = dict[part] + text = dict for name, value of options text = text.split("%{#{name}}").join(value) text diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index 9d7a453b18..acbd0fdd01 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -5,7 +5,7 @@ module Api has_many :payments, serializer: Api::PaymentSerializer def completed_at - object.completed_at.blank? ? "" : I18n.l(object.completed_at, format: :long) #.to_formatted_s(:long_ordinal) + object.completed_at.blank? ? "" : I18n.l(object.completed_at, format: :long) end def total @@ -13,15 +13,15 @@ module Api end def shipment_state - object.shipment_state ? object.shipment_state.humanize : nil # Or a call to t() here? + object.shipment_state ? object.shipment_state : nil end def payment_state - object.payment_state ? object.payment_state.humanize : nil # Or a call to t() here? + object.payment_state ? object.payment_state : nil end def state - object.state ? object.state.humanize : nil # Or a call to t() here? + object.state ? object.state : nil end def path diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index c31a16c9ec..94088e2bef 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -14,8 +14,8 @@ %td.order1 %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} %td.order2{"bo-text" => "order.completed_at"} - %td.order3.show-for-large-up{"bo-text" => "order.payment_state | t"} - %td.order4.show-for-large-up{"bo-text" => "order.shipment_state | t"} + %td.order3.show-for-large-up{"bo-text" => "'spree.payment_states.' + order.payment_state | t"} + %td.order4.show-for-large-up{"bo-text" => "'spree.shipment_states.' + order.shipment_state | t"} %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} %td.order6.text-right.show-for-large-up{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} diff --git a/app/views/spree/users/_skinny.html.haml b/app/views/spree/users/_skinny.html.haml index aee1884746..14c04f024a 100644 --- a/app/views/spree/users/_skinny.html.haml +++ b/app/views/spree/users/_skinny.html.haml @@ -7,7 +7,6 @@ %strong{"bo-text" => "distributor.name"} .columns.small-8.small-offset-2.medium-3.text-right %span.margin-top.distributor-balance{"bo-text" => "distributor.balance | formatBalance", "ng-class" => "{'credit' : distributor.balance < 0, 'debit' : distributor.balance > 0, 'paid' : distributor.balance == 0}" } - -# %span.margin-top{"bo-text" => "('balance' | t) + ': ' + Orders.currency_symbol + distributor.balance", "ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}"} .columns.small-2.medium-2.text-right %span.margin-top %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} diff --git a/config/locales/en.yml b/config/locales/en.yml index 4fb740cde6..fb79dfe1fc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -689,3 +689,34 @@ Please follow the instructions there to make your enterprise visible on the Open you_have_no_orders_yet: "You have no orders yet" running_balance: "Running balance" outstanding_balance: "Outstanding balance" + + spree: + shipment_states: + backorder: backorder + partial: partial + pending: pending + ready: ready + shipped: shipped + payment_states: + balance_due: balance due + completed: completed + checkout: checkout + credit_owed: credit owed + failed: failed + paid: paid + pending: pending + processing: processing + void: void + order_state: + address: address + adjustments: adjustments + awaiting_return: awaiting return + canceled: canceled + cart: cart + complete: complete + confirm: confirm + delivery: delivery + payment: payment + resumed: resumed + returned: returned + skrill: skrill diff --git a/spec/serializers/enterprise_serializer_spec.rb b/spec/serializers/enterprise_serializer_spec.rb index f70852d973..f5eff4f771 100644 --- a/spec/serializers/enterprise_serializer_spec.rb +++ b/spec/serializers/enterprise_serializer_spec.rb @@ -1,4 +1,4 @@ -#require 'spec_helper' +require 'spec_helper' describe Api::EnterpriseSerializer do let(:serializer) { Api::EnterpriseSerializer.new enterprise, data: data } diff --git a/spec/serializers/order_serializer_spec.rb b/spec/serializers/order_serializer_spec.rb index 1b7c99715b..2c327537eb 100644 --- a/spec/serializers/order_serializer_spec.rb +++ b/spec/serializers/order_serializer_spec.rb @@ -1,4 +1,4 @@ -#require 'spec_helper' +require 'spec_helper' describe Api::OrderSerializer do let(:serializer) { Api::OrderSerializer.new order } @@ -9,9 +9,9 @@ describe Api::OrderSerializer do expect(serializer.to_json).to match order.number.to_s end - it "convert the state attributes to readable strings" do - expect(serializer.to_json).to match "Complete" - expect(serializer.to_json).to match "Balance due" + it "convert the state attributes to translatable keys" do + expect(serializer.to_json).to match "complete" + expect(serializer.to_json).to match "balance_due" end end diff --git a/spec/serializers/orders_by_distributor_serializer_spec.rb b/spec/serializers/orders_by_distributor_serializer_spec.rb index 4a75f76fea..5976e9ad48 100644 --- a/spec/serializers/orders_by_distributor_serializer_spec.rb +++ b/spec/serializers/orders_by_distributor_serializer_spec.rb @@ -1,4 +1,4 @@ -#require 'spec_helper' +require 'spec_helper' describe Api::OrdersByDistributorSerializer do From f1cc3a7b0df6dd12123f14d9d54fc7b09a520c68 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 9 Mar 2016 12:42:16 +1100 Subject: [PATCH 125/215] Apply code style following Rubocop --- app/helpers/injection_helper.rb | 4 ++-- app/models/spree/user_decorator.rb | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index aa5148b394..7f14fa46f2 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -56,10 +56,10 @@ module InjectionHelper data_array = spree_current_user.orders_by_distributor.to_a data_array.each do |enterprise| enterprise.distributed_orders.each do |order| - order.payments.keep_if{ |payment| payment.state == "completed" } + order.payments.keep_if { |payment| payment.state == "completed" } end end - data_array.sort!{ |a, b| b.distributed_orders.length <=> a.distributed_orders.length } + data_array.sort! { |a, b| b.distributed_orders.length <=> a.distributed_orders.length } inject_json_ams "orders_by_distributor", data_array, Api::OrdersByDistributorSerializer end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 6c5027a228..e4bac45990 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -56,7 +56,11 @@ Spree.user_class.class_eval do # Returns orders and their associated payments for all distributors that have been ordered from def orders_by_distributor - Enterprise.includes(distributed_orders: :payments).where(enterprises: { id: self.enterprises_ordered_from }, spree_orders: { state: 'complete', user_id: self.id }).order('spree_orders.completed_at DESC') + Enterprise.includes(distributed_orders: :payments). + where( + enterprises: { id: self.enterprises_ordered_from }, + spree_orders: { state: 'complete', user_id: self.id }). + order('spree_orders.completed_at DESC') end private From 9ce5c45799489b166c76546aafb30f764f258420 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 9 Mar 2016 14:05:38 +1100 Subject: [PATCH 126/215] Update gem nokogiri to 1.6.7.2 Previous versions had security vulnerabilities. See: http://rubysec.com/advisories/CVE-2015-5312/ --- Gemfile | 3 ++- Gemfile.lock | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 8230c6a15f..89f1fc61eb 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,8 @@ gem 'rails', '3.2.21' gem 'rails-i18n', '~> 3.0.0' gem 'i18n', '~> 0.6.11' -gem 'nokogiri' +# Patched version. See http://rubysec.com/advisories/CVE-2015-5312/. +gem 'nokogiri', '>= 1.6.7.1' gem 'pg' gem 'spree', :github => 'openfoodfoundation/spree', :branch => '1-3-stable' diff --git a/Gemfile.lock b/Gemfile.lock index 37677c043c..694fa89f56 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -449,7 +449,7 @@ GEM treetop (~> 1.4.8) method_source (0.8.2) mime-types (1.25.1) - mini_portile (0.6.2) + mini_portile2 (2.0.0) momentjs-rails (2.5.1) railties (>= 3.1) money (5.1.1) @@ -457,8 +457,8 @@ GEM multi_json (1.11.2) multi_xml (0.5.5) newrelic_rpm (3.12.0.288) - nokogiri (1.6.6.4) - mini_portile (~> 0.6.0) + nokogiri (1.6.7.2) + mini_portile2 (~> 2.0.0.rc2) oj (2.1.2) orm_adapter (0.5.0) paper_trail (3.0.8) @@ -691,7 +691,7 @@ DEPENDENCIES letter_opener momentjs-rails newrelic_rpm - nokogiri + nokogiri (>= 1.6.7.1) oj paper_trail (~> 3.0.8) paperclip @@ -730,3 +730,6 @@ DEPENDENCIES whenever wicked_pdf wkhtmltopdf-binary + +BUNDLED WITH + 1.10.6 From b6406b54b4522464175bbc5627f348d9afb3b289 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 9 Mar 2016 14:40:04 +1100 Subject: [PATCH 127/215] Apply code style --- app/models/spree/user_decorator.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index e4bac45990..16d1c1c6c9 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -56,11 +56,11 @@ Spree.user_class.class_eval do # Returns orders and their associated payments for all distributors that have been ordered from def orders_by_distributor - Enterprise.includes(distributed_orders: :payments). - where( + Enterprise.includes(distributed_orders: :payments) + .where( enterprises: { id: self.enterprises_ordered_from }, - spree_orders: { state: 'complete', user_id: self.id }). - order('spree_orders.completed_at DESC') + spree_orders: { state: 'complete', user_id: self.id }) + .order('spree_orders.completed_at DESC') end private From 071ee9ab546c17e4fd844e8a3dd0e4386b028926 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 9 Mar 2016 18:39:23 +1100 Subject: [PATCH 128/215] Add brackets to `t` call in js line items controller The missing brackets made all columns of the bulk order management invisible. Also removed trailing whitespaces. --- .../javascripts/admin/enterprise_fees.js | 4 +-- .../enterprise_controller.js.coffee | 2 -- .../enterprises_controller.js.coffee | 1 - .../line_items_controller.js.coffee | 32 +++++++++---------- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/admin/enterprise_fees.js b/app/assets/javascripts/admin/enterprise_fees.js index ae21d04009..7eb249f4ec 100644 --- a/app/assets/javascripts/admin/enterprise_fees.js +++ b/app/assets/javascripts/admin/enterprise_fees.js @@ -34,9 +34,7 @@ angular.module('enterprise_fees', []) return function(scope, element, attrs) { if(scope.enterprise_fee.id) { var url = "/admin/enterprise_fees/" + scope.enterprise_fee.id - var html = ''; - var html = ''; - //var html = 'Delete Delete'; + var html = ''; element.append(html); } } diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee index 34b4f99ca2..a806691465 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee @@ -32,5 +32,3 @@ angular.module("admin.enterprises") $scope.Enterprise.users.push manager else alert ("#{manager.email}" + " " + t("is_already_manager")) - - diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee index 30710d1538..3d8bfa6446 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee @@ -11,4 +11,3 @@ angular.module("admin.enterprises").controller 'enterprisesCtrl', ($scope, $q, E package: { name: "Package", visible: true } status: { name: "Status", visible: true } manage: { name: "Manage", visible: true } - diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index d5b71f8c57..d758706d65 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -5,24 +5,24 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.confirmDelete = true $scope.startDate = formatDate daysFromToday -7 $scope.endDate = formatDate daysFromToday 1 - $scope.bulkActions = [ { name: t "bom_actions_delete", callback: 'deleteLineItems' } ] - $scope.selectedUnitsProduct = {}; - $scope.selectedUnitsVariant = {}; + $scope.bulkActions = [ { name: t("bom_actions_delete"), callback: 'deleteLineItems' } ] + $scope.selectedUnitsProduct = {} + $scope.selectedUnitsVariant = {} $scope.sharedResource = false $scope.columns = Columns.setColumns - order_no: { name: t "bom_no", visible: false } - full_name: { name: t "name", visible: true } - email: { name: t "email", visible: false } - phone: { name: t "phone", visible: false } - order_date: { name: t "bom_date", visible: true } - producer: { name: t "producer", visible: true } - order_cycle: { name: t "bom_cycle", visible: false } - hub: { name: t "bom_hub", visible: false } - variant: { name: t "bom_variant", visible: true } - quantity: { name: t "bom_quantity", visible: true } - max: { name: t "bom_max", visible: true } - final_weight_volume: { name: t "bom_final_weigth_volume", visible: false } - price: { name: t "bom_price", visible: false } + order_no: { name: t("bom_no"), visible: false } + full_name: { name: t("name"), visible: true } + email: { name: t("email"), visible: false } + phone: { name: t("phone"), visible: false } + order_date: { name: t("bom_date"), visible: true } + producer: { name: t("producer"), visible: true } + order_cycle: { name: t("bom_cycle"), visible: false } + hub: { name: t("bom_hub"), visible: false } + variant: { name: t("bom_variant"), visible: true } + quantity: { name: t("bom_quantity"), visible: true } + max: { name: t("bom_max"), visible: true } + final_weight_volume: { name: t("bom_final_weigth_volume"), visible: false } + price: { name: t("bom_price"), visible: false } $scope.confirmRefresh = -> LineItems.allSaved() || confirm(t "unsaved_changes_warning") From 02cbad26976de16c9f5e081c604252c859bba9bc Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 9 Mar 2016 18:49:35 +1100 Subject: [PATCH 129/215] Remove duplicate lines from merging --- app/models/customer.rb | 1 - .../accounts_and_billing_settings/edit.html.haml | 15 +++++++-------- app/views/admin/customers/index.html.haml | 6 +++--- app/views/admin/enterprise_fees/index.html.haml | 12 ++++++------ .../admin/enterprise_groups/_form_about.html.haml | 2 +- .../enterprise_groups/_form_address.html.haml | 2 +- .../enterprise_groups/_form_images.html.haml | 4 ++-- .../_form_primary_details.html.haml | 2 +- .../admin/enterprise_groups/_form_users.html.haml | 2 +- .../admin/enterprise_groups/_form_web.html.haml | 2 +- app/views/admin/enterprise_groups/index.html.haml | 6 +++--- .../_enterprise_relationship.html.haml | 2 +- .../admin/order_cycles/set_coordinator.html.haml | 2 +- .../admin/overview/_enterprises_header.html.haml | 5 ++--- 14 files changed, 30 insertions(+), 33 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index 1ab2f3ae5c..c4c93cb8aa 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -18,4 +18,3 @@ class Customer < ActiveRecord::Base self.user = user || Spree::User.find_by_email(email) end end - diff --git a/app/views/admin/accounts_and_billing_settings/edit.html.haml b/app/views/admin/accounts_and_billing_settings/edit.html.haml index 7a74a59120..cd614b14f8 100644 --- a/app/views/admin/accounts_and_billing_settings/edit.html.haml +++ b/app/views/admin/accounts_and_billing_settings/edit.html.haml @@ -8,7 +8,7 @@ -# - month_options = (0...12).map { |i| Time.zone.now.beginning_of_month - i.months }.map{ |t| [t.strftime("%b %Y"), t.strftime("%b %Y %z")]} %fieldset.no-border-bottom - %legend + %legend =t :admin_settings = form_for @settings, as: :settings, url: main_app.admin_accounts_and_billing_settings_path, :method => :put do |f| .row{ ng: { app: t(:admin_accounts_and_billing) } } @@ -24,7 +24,7 @@ .row .six.columns.alpha %fieldset.no-border-bottom - %legend + %legend =t :update_invoice = f.check_box :auto_update_invoices = f.label :auto_update_invoices, @@ -32,7 +32,7 @@ .six.columns.omega %fieldset.no-border-bottom - %legend + %legend =t :finalise_invoice = f.check_box :auto_finalize_invoices = f.label :auto_finalize_invoices, @@ -48,7 +48,7 @@ .row .six.columns.alpha.step.text-center .form-buttons{"data-hook" => "buttons"} - =link_to_with_icon "icon-undo", + =link_to_with_icon "icon-undo", t(:update_user_invoices) , main_app.start_job_admin_accounts_and_billing_settings_path(job: { name: "update_account_invoices" }), class: "button fullwidth" @@ -62,7 +62,7 @@ %br =t :started_at - else - %strong + %strong =t :queued %br =t :Scheduled_for @@ -82,12 +82,12 @@ - if @finalize_account_invoices_job %p.text-center - if @finalize_account_invoices_job.run_at < Time.zone.now - %strong + %strong =t :in_progress %br =t :started_at - else - %strong + %strong =t :queued %br =t :scheduled_for @@ -95,4 +95,3 @@ - else %p.explanation =t :finalise_user_invoice_explained - diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index ddeddd9025..cf3ef25bb3 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -1,5 +1,5 @@ - content_for :page_title do - %h1.page-title + %h1.page-title =t :customers = admin_inject_shops @@ -7,7 +7,7 @@ %div{ ng: { app: 'admin.customers', controller: 'customersCtrl' } } .row{ ng: { hide: "loaded() && filteredCustomers.length > 0" } } .five.columns.alpha - %h3 + %h3 =t :please_select_hub .four.columns %select.select2.fullwidth#shop_id{ 'ng-model' => 'shop.id', name: 'shop_id', 'ng-options' => 'shop.id as shop.name for shop in shops' } @@ -27,7 +27,7 @@ %h1 =t :loading_customers .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded() && filteredCustomers.length == 0'} - %h1#no_results + %h1#no_results =t :no_customers_found diff --git a/app/views/admin/enterprise_fees/index.html.haml b/app/views/admin/enterprise_fees/index.html.haml index 42966bd6be..c9d1311d7a 100644 --- a/app/views/admin/enterprise_fees/index.html.haml +++ b/app/views/admin/enterprise_fees/index.html.haml @@ -10,17 +10,17 @@ %table.index#listing_enterprise_fees %thead %tr - %th + %th =t'Enterprise' - %th + %th =t'fee_type' - %th + %th =t'name' - %th + %th =t'tax_category' - %th + %th =t'calculator' - %th + %th =t'calculator_values' %th.actions %tbody diff --git a/app/views/admin/enterprise_groups/_form_about.html.haml b/app/views/admin/enterprise_groups/_form_about.html.haml index 2c2ba59464..1e97697a4e 100644 --- a/app/views/admin/enterprise_groups/_form_about.html.haml +++ b/app/views/admin/enterprise_groups/_form_about.html.haml @@ -1,5 +1,5 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='About'" } } - %legend + %legend = t 'admin_entreprise_groups_about' = f.field_container :long_description do %text-angular{'id' => 'enterprise_group_long_description', 'name' => 'enterprise_group[long_description]', 'class' => 'text-angular', diff --git a/app/views/admin/enterprise_groups/_form_address.html.haml b/app/views/admin/enterprise_groups/_form_address.html.haml index a161c1d619..fa7b1e7e75 100644 --- a/app/views/admin/enterprise_groups/_form_address.html.haml +++ b/app/views/admin/enterprise_groups/_form_address.html.haml @@ -1,6 +1,6 @@ = f.fields_for :address do |af| %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Contact'" } } - %legend + %legend = t 'admin_entreprise_groups_contact' .row .alpha.three.columns diff --git a/app/views/admin/enterprise_groups/_form_images.html.haml b/app/views/admin/enterprise_groups/_form_images.html.haml index ee01267208..b24feb60a0 100644 --- a/app/views/admin/enterprise_groups/_form_images.html.haml +++ b/app/views/admin/enterprise_groups/_form_images.html.haml @@ -1,5 +1,5 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Images'" } } - %legend + %legend = t 'admin_entreprise_groups_images' .row .alpha.three.columns @@ -13,7 +13,7 @@ .alpha.three.columns = f.label :promo_image, class: 'with-tip', 'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image) .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image_tip)} - %a + %a = t 'admin_entreprise_groups_what_s_this' = f.label :promo_image, 'ofn-with-tip' => 'This image is displayed at the top of the Group profile' %div{'ofn-with-tip' => 'This image is displayed at the top of the Group profile'} 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 6785d88ada..56771c458c 100644 --- a/app/views/admin/enterprise_groups/_form_primary_details.html.haml +++ b/app/views/admin/enterprise_groups/_form_primary_details.html.haml @@ -1,5 +1,5 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Primary Details'" } } - %legend + %legend = t "admin_entreprise_groups_primary_details" = f.field_container :name do = f.label :name diff --git a/app/views/admin/enterprise_groups/_form_users.html.haml b/app/views/admin/enterprise_groups/_form_users.html.haml index 3d146ed6f9..4c111c1772 100644 --- a/app/views/admin/enterprise_groups/_form_users.html.haml +++ b/app/views/admin/enterprise_groups/_form_users.html.haml @@ -4,7 +4,7 @@ .three.columns.alpha =f.label :owner_id, t(:admin_entreprise_groups_owner) .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip)} - %a + %a = t 'admin_entreprise_groups_what_s_this' =f.label :owner_id, 'Owner' %div{'ofn-with-tip' => "The primary user responsible for this group."} diff --git a/app/views/admin/enterprise_groups/_form_web.html.haml b/app/views/admin/enterprise_groups/_form_web.html.haml index 4d20f9d2fd..5d982f1bbb 100644 --- a/app/views/admin/enterprise_groups/_form_web.html.haml +++ b/app/views/admin/enterprise_groups/_form_web.html.haml @@ -1,5 +1,5 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Web'" } } - %legend + %legend = t 'admin_entreprise_groups_web' .row .alpha.three.columns diff --git a/app/views/admin/enterprise_groups/index.html.haml b/app/views/admin/enterprise_groups/index.html.haml index 932695201f..a4eddfd5ca 100644 --- a/app/views/admin/enterprise_groups/index.html.haml +++ b/app/views/admin/enterprise_groups/index.html.haml @@ -8,14 +8,14 @@ %table.index#listing_enterprise_groups %thead %tr - %th + %th = t 'admin_entreprise_groups_name' - if spree_current_user.admin? %th = t 'admin_entreprise_groups_owner' - %th + %th = t 'admin_entreprise_groups_on_front_page' - %th + %th = t 'admin_entreprise_groups_entreprise' %th.actions diff --git a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml index b4e865f083..05e6505868 100644 --- a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml +++ b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml @@ -1,6 +1,6 @@ %tr{"ng-repeat" => "enterprise_relationship in EnterpriseRelationships.enterprise_relationships | keywords:query"} %td {{ enterprise_relationship.parent_name }} - %td + %td = t 'admin_entreprise_relationships_permits' %td {{ enterprise_relationship.child_name }} %td diff --git a/app/views/admin/order_cycles/set_coordinator.html.haml b/app/views/admin/order_cycles/set_coordinator.html.haml index 0216edee54..db9bbb926c 100644 --- a/app/views/admin/order_cycles/set_coordinator.html.haml +++ b/app/views/admin/order_cycles/set_coordinator.html.haml @@ -1,4 +1,4 @@ -%h4.text-center +%h4.text-center =t'select_a_coordinator_for_your_order_cycle' %br diff --git a/app/views/spree/admin/overview/_enterprises_header.html.haml b/app/views/spree/admin/overview/_enterprises_header.html.haml index 78959383c5..5b1979c4ce 100644 --- a/app/views/spree/admin/overview/_enterprises_header.html.haml +++ b/app/views/spree/admin/overview/_enterprises_header.html.haml @@ -1,11 +1,10 @@ %div.header.sixteen.columns.alpha{ :class => "#{@enterprises.count > 0 ? "" : "red"}"} - %h3.thirteen.columns.alpha + %h3.thirteen.columns.alpha = t "spree_admin_overview_enterprises_header" - if @enterprises.any? - if spree_current_user.can_own_more_enterprises? %a.three.columns.omega.icon-plus.button.blue.white-bottom{ href: "#{main_app.new_admin_enterprise_path}" } = t "spree_admin_enterprises_create_new" - else - %a.with-tip{ title: "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } + %a{ "ofn-with-tip" => "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } = t "admin_enterprise_groups_what_s_this" - %a{ "ofn-with-tip" => "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } What's this? From 02cc5adf78787092bd6f34127718b1b262cd2147 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 9 Mar 2016 18:57:54 +1100 Subject: [PATCH 130/215] Revert false HAML indent --- .../admin/overview/_enterprises_hubs_tab.html.haml | 4 ++-- .../overview/_enterprises_producers_tab.html.haml | 4 ++-- .../spree/admin/overview/_enterprises_tabs.html.haml | 2 +- app/views/spree/admin/overview/_order_cycles.html.haml | 4 ++-- app/views/spree/admin/overview/_products.html.haml | 7 ++----- .../overview/multi_enterprise_dashboard.html.haml | 5 ++--- .../overview/single_enterprise_dashboard.html.haml | 10 +++++----- app/views/spree/admin/reports/packing.html.haml | 2 +- app/views/spree/admin/reports/payments.html.haml | 2 +- .../admin/reports/products_and_inventory.html.haml | 2 +- app/views/spree/admin/reports/sales_tax.html.haml | 2 +- app/views/spree/admin/shared/_hubs_sidebar.html.haml | 2 +- 12 files changed, 21 insertions(+), 25 deletions(-) diff --git a/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml b/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml index b8ad4b0c72..3073ec78a3 100644 --- a/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml +++ b/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml @@ -1,11 +1,11 @@ %div.hubs_tab{ ng: { show: "activeTab == 'hubs'"} } %div.sixteen.columns.alpha.list-title - %span.five.columns.alpha + %span.five.columns.alpha = t "spree_admin_enterprises_hubs_name" - if can? :admin, Spree::PaymentMethod %span.centered.three.columns Payment Methods - if can? :admin, Spree::ShippingMethod - %span.centered.three.columns + %span.centered.three.columns = t "spree_admin_enterprises_shipping_methods" - if can? :admin, EnterpriseFee %span.centered.three.columns diff --git a/app/views/spree/admin/overview/_enterprises_producers_tab.html.haml b/app/views/spree/admin/overview/_enterprises_producers_tab.html.haml index 7ff6c8b911..19cae1e6f8 100644 --- a/app/views/spree/admin/overview/_enterprises_producers_tab.html.haml +++ b/app/views/spree/admin/overview/_enterprises_producers_tab.html.haml @@ -1,11 +1,11 @@ %div.producers_tab{ ng: { show: "activeTab == 'producers'"} } %div.list-title.sixteen.columns.alpha - %span.five.columns.alpha + %span.five.columns.alpha = t "spree_admin_enterprises_producers_name" - if can? :admin, Spree::Product %span.centered.three.columns = t "spree_admin_enterprises_producers_total_products" - %span.centered.three.columns + %span.centered.three.columns = t "spree_admin_enterprises_producers_active_products" - if can? :admin, OrderCycle %span.centered.three.columns diff --git a/app/views/spree/admin/overview/_enterprises_tabs.html.haml b/app/views/spree/admin/overview/_enterprises_tabs.html.haml index 1124c90f30..5f8eb9e3c2 100644 --- a/app/views/spree/admin/overview/_enterprises_tabs.html.haml +++ b/app/views/spree/admin/overview/_enterprises_tabs.html.haml @@ -1,5 +1,5 @@ %div.sixteen.columns.alpha.tabs - %div.dashboard_tab.eight.columns.alpha.blue{ ng: { class: "{selected: activeTab == 'hubs'}", click: "activeTab = 'hubs'" } } + %div.dashboard_tab.eight.columns.alpha.blue{ ng: { class: "{selected: activeTab == 'hubs'}", click: "activeTab = 'hubs'" } } = t "spree_admin_enterprises_tabs_hubs" %div.dashboard_tab.eight.columns.omega.blue{ ng: { class: "{selected: activeTab == 'producers'}", click: "activeTab = 'producers'" } } = t "spree_admin_enterprises_tabs_producers" diff --git a/app/views/spree/admin/overview/_order_cycles.html.haml b/app/views/spree/admin/overview/_order_cycles.html.haml index 63694e5625..ae4c9c2c0b 100644 --- a/app/views/spree/admin/overview/_order_cycles.html.haml +++ b/app/views/spree/admin/overview/_order_cycles.html.haml @@ -6,7 +6,7 @@ %a.three.columns.omega.icon-plus.button.blue{ href: "#{main_app.new_admin_order_cycle_path}" } = t "spree_admin_enterprises_create_new" - else - %a.with-tip{ title: t(:spree_admin_order_cycles_tip) } + %a.with-tip{ title: t(:spree_admin_order_cycles_tip) } = t "admin_entreprise_groups_what_s_this" %a{ "ofn-with-tip" => "Order cycles determine when and where your products are available to customers." } What's this? %div.seven.columns.alpha.list @@ -21,7 +21,7 @@ %span.icon-arrow-right - else %div.seven.columns.alpha.list-item.orange - %span.six.columns.alpha + %span.six.columns.alpha = t "spree_admin_enterprises_producers_orders_cycle_text" %span.one.column.omega %span.icon-warning-sign diff --git a/app/views/spree/admin/overview/_products.html.haml b/app/views/spree/admin/overview/_products.html.haml index 2e4a159373..a85132bdf3 100644 --- a/app/views/spree/admin/overview/_products.html.haml +++ b/app/views/spree/admin/overview/_products.html.haml @@ -6,9 +6,8 @@ %a.three.columns.omega.icon-plus.button.blue{ href: "#{new_admin_product_path}" } = t "spree_admin_enterprises_create_new" - else - %a.with-tip{ title: "The products that you sell through the Open Food Network." } + %a{ "ofn-with-tip" => "The products that you sell through the Open Food Network." } = t "admin_entreprise_groups_what_s_this" - %a{ "ofn-with-tip" => "The products that you sell through the Open Food Network." } What's this? %div.seven.columns.alpha.list - if @product_count > 0 %div.seven.columns.alpha.list-item @@ -21,12 +20,10 @@ %span.icon-arrow-right - else %div.seven.columns.alpha.list-item.red - %span.six.columns.alpha + %span.six.columns.alpha = t "spree_admin_enterprises_any_active_products_text" %span.one.column.omega %span.icon-remove-sign %a.seven.columns.alpha.button.bottom.red{ href: "#{new_admin_product_path}" } = t "spree_admin_enterprises_create_new_product" %span.icon-arrow-right - CREATE A NEW PRODUCT - %span.icon-arrow-right diff --git a/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml index 202443343d..0647304e05 100644 --- a/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml @@ -2,10 +2,9 @@ = render 'admin/shared/user_guide_link' -%h1{ :style => 'margin-bottom: 30px'} - = t "dashbord" %div{ 'ng-app' => 'ofn.admin' } - %h1{ :style => 'margin-bottom: 30px' } Dashboard + %h1{ :style => 'margin-bottom: 30px' } + = t "dashbord" - if @enterprises.unconfirmed.any? diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml index d2b0635409..91c0934e21 100644 --- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml @@ -37,7 +37,7 @@ %a.close{ href: "#" } × - if !@enterprise.visible .alert-box - %strong + %strong = t "spree_admin_single_enterprise_hint" %strong= "#{t 'manage'} #{@enterprise.name}." %a.close{ href: "#" } × @@ -47,7 +47,7 @@ .header %h3 %span.icon-map-marker - = t "your_profil_live" + = t "your_profil_live" %p = t "on_ofn_map" .list @@ -64,7 +64,7 @@ .header %h3 %span.icon-edit - = t "edit_profile_details" + = t "edit_profile_details" %p = t "edit_profile_details_etc" .list @@ -79,7 +79,7 @@ .header %h3 %span.icon-th-large - = t "add_and_manage_products" + = t "add_and_manage_products" .list %a.button.bottom{href: bulk_edit_admin_products_path} = t "manage_products" @@ -93,7 +93,7 @@ .header %h3 %span.icon-shopping-cart - = t "add_and_manage_order_cycles" + = t "add_and_manage_order_cycles" .list %a.button.bottom{href: main_app.admin_order_cycles_path} = t "manage_order_cycles" diff --git a/app/views/spree/admin/reports/packing.html.haml b/app/views/spree/admin/reports/packing.html.haml index d419f9a7cb..fa9d22eb70 100644 --- a/app/views/spree/admin/reports/packing.html.haml +++ b/app/views/spree/admin/reports/packing.html.haml @@ -29,7 +29,7 @@ %br %table#listing_orders.index %thead - %tr{'data-hook' => "orders_header" } + %tr{'data-hook' => "orders_header"} - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/payments.html.haml b/app/views/spree/admin/reports/payments.html.haml index a0e8f1d1d8..b69708fbd1 100644 --- a/app/views/spree/admin/reports/payments.html.haml +++ b/app/views/spree/admin/reports/payments.html.haml @@ -19,7 +19,7 @@ %br %table#listing_orders.index %thead - %tr{'data-hook' => "orders_header" } + %tr{'data-hook' => "orders_header"} - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/products_and_inventory.html.haml b/app/views/spree/admin/reports/products_and_inventory.html.haml index 53ad6649ee..b1c11918ce 100644 --- a/app/views/spree/admin/reports/products_and_inventory.html.haml +++ b/app/views/spree/admin/reports/products_and_inventory.html.haml @@ -35,7 +35,7 @@ %br %table#listing_products.index %thead - %tr{'data-hook' => "products_header" } + %tr{'data-hook' => "products_header"} - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/sales_tax.html.haml b/app/views/spree/admin/reports/sales_tax.html.haml index c91617cc6a..9a5cb9e533 100644 --- a/app/views/spree/admin/reports/sales_tax.html.haml +++ b/app/views/spree/admin/reports/sales_tax.html.haml @@ -14,7 +14,7 @@ %br %table#listing_orders.index %thead - %tr{'data-hook' => "orders_header" } + %tr{'data-hook' => "orders_header"} - @report.header.each do |heading| %th= heading %tbody diff --git a/app/views/spree/admin/shared/_hubs_sidebar.html.haml b/app/views/spree/admin/shared/_hubs_sidebar.html.haml index 25cdd89685..9bc54be829 100644 --- a/app/views/spree/admin/shared/_hubs_sidebar.html.haml +++ b/app/views/spree/admin/shared/_hubs_sidebar.html.haml @@ -17,7 +17,7 @@ %span.icon-arrow-right - else .four.columns.alpha.list-item - %span.three.columns.alpha + %span.three.columns.alpha t(:hub_sidebar_none_available) %span.one.column.omega %span.icon-remove-sign From fe3c0b8d29d7088d58bdbc3680da9207980d7b58 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Thu, 10 Mar 2016 21:42:42 +0000 Subject: [PATCH 131/215] Move data processing to model, add payment method preload --- app/helpers/injection_helper.rb | 9 +-------- app/models/spree/user_decorator.rb | 13 ++++++++++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index aa5148b394..c6be5dc63e 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -52,14 +52,7 @@ module InjectionHelper end def inject_orders_by_distributor - # Convert ActiveRecord::Relation to array for serialization - data_array = spree_current_user.orders_by_distributor.to_a - data_array.each do |enterprise| - enterprise.distributed_orders.each do |order| - order.payments.keep_if{ |payment| payment.state == "completed" } - end - end - data_array.sort!{ |a, b| b.distributed_orders.length <=> a.distributed_orders.length } + data_array = spree_current_user.orders_by_distributor inject_json_ams "orders_by_distributor", data_array, Api::OrdersByDistributorSerializer end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 6c5027a228..7fb5d7ddb1 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -56,7 +56,18 @@ Spree.user_class.class_eval do # Returns orders and their associated payments for all distributors that have been ordered from def orders_by_distributor - Enterprise.includes(distributed_orders: :payments).where(enterprises: { id: self.enterprises_ordered_from }, spree_orders: { state: 'complete', user_id: self.id }).order('spree_orders.completed_at DESC') + data_array = Enterprise.includes(distributed_orders: {payments: :payment_method}) + .where(enterprises: { id: self.enterprises_ordered_from }, + spree_orders: { state: 'complete', user_id: self.id} + ).order('spree_orders.completed_at DESC') + .to_a + # Remove uncompleted payments as these will not be reflected in order balance + data_array.each do |enterprise| + enterprise.distributed_orders.each do |order| + order.payments.keep_if{ |payment| payment.state == "completed" } + end + end + data_array.sort!{ |a, b| b.distributed_orders.length <=> a.distributed_orders.length } end private From 3595685f9a6ad3293fb34b674787f8e20f4d1b45 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Mar 2016 11:46:06 +1100 Subject: [PATCH 132/215] Merge text change in LineItemsCtrl into en.yml --- config/locales/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index e26c543935..608bbd86ea 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -255,7 +255,7 @@ en: bom_no_results: "No orders found." bom_order_error: "Some errors must be resolved before you can update orders.\nAny fields with red borders contain errors." - unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." + unsaved_changes_warning: "Fields with red borders contain errors." products: "Products" products_in: "in %{oc}" From 3e5f53b0827272da9afe05ed9161e4e97a1fec1a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Mar 2016 12:13:17 +1100 Subject: [PATCH 133/215] Rename i18n key what_is_this --- app/views/admin/enterprise_groups/_form_images.html.haml | 2 +- app/views/admin/enterprise_groups/_form_users.html.haml | 2 +- app/views/spree/admin/orders/bulk_management.html.haml | 6 ++---- app/views/spree/admin/overview/_order_cycles.html.haml | 2 +- app/views/spree/admin/overview/_products.html.haml | 2 +- config/locales/en.yml | 3 +-- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/app/views/admin/enterprise_groups/_form_images.html.haml b/app/views/admin/enterprise_groups/_form_images.html.haml index b24feb60a0..67378f331b 100644 --- a/app/views/admin/enterprise_groups/_form_images.html.haml +++ b/app/views/admin/enterprise_groups/_form_images.html.haml @@ -14,7 +14,7 @@ = f.label :promo_image, class: 'with-tip', 'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image) .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image_tip)} %a - = t 'admin_entreprise_groups_what_s_this' + = t 'what_is_this' = f.label :promo_image, 'ofn-with-tip' => 'This image is displayed at the top of the Group profile' %div{'ofn-with-tip' => 'This image is displayed at the top of the Group profile'} %a What's this? diff --git a/app/views/admin/enterprise_groups/_form_users.html.haml b/app/views/admin/enterprise_groups/_form_users.html.haml index 4c111c1772..082fa2d3bf 100644 --- a/app/views/admin/enterprise_groups/_form_users.html.haml +++ b/app/views/admin/enterprise_groups/_form_users.html.haml @@ -5,7 +5,7 @@ =f.label :owner_id, t(:admin_entreprise_groups_owner) .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip)} %a - = t 'admin_entreprise_groups_what_s_this' + = t 'what_is_this' =f.label :owner_id, 'Owner' %div{'ofn-with-tip' => "The primary user responsible for this group."} %a What's this? diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 9e9362681d..b0908e0b6e 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -2,12 +2,10 @@ = "ng-app='admin.lineItems'" - content_for :page_title do - %h1.page-title - = t "bulk_order_management" - %a{ 'ofn-with-tip' => "#t('bom_tip')" } - = t "admin_entreprise_groups_what_s_this" %h1.page-title = t "bom_page_title" + %a{ 'ofn-with-tip' => "#t('bom_tip')" } + = t "what_is_this" = render :partial => 'spree/admin/shared/order_sub_menu' diff --git a/app/views/spree/admin/overview/_order_cycles.html.haml b/app/views/spree/admin/overview/_order_cycles.html.haml index ae4c9c2c0b..9be7fffed2 100644 --- a/app/views/spree/admin/overview/_order_cycles.html.haml +++ b/app/views/spree/admin/overview/_order_cycles.html.haml @@ -7,7 +7,7 @@ = t "spree_admin_enterprises_create_new" - else %a.with-tip{ title: t(:spree_admin_order_cycles_tip) } - = t "admin_entreprise_groups_what_s_this" + = t "what_is_this" %a{ "ofn-with-tip" => "Order cycles determine when and where your products are available to customers." } What's this? %div.seven.columns.alpha.list - if @order_cycle_count > 0 diff --git a/app/views/spree/admin/overview/_products.html.haml b/app/views/spree/admin/overview/_products.html.haml index a85132bdf3..79da1a0f1b 100644 --- a/app/views/spree/admin/overview/_products.html.haml +++ b/app/views/spree/admin/overview/_products.html.haml @@ -7,7 +7,7 @@ = t "spree_admin_enterprises_create_new" - else %a{ "ofn-with-tip" => "The products that you sell through the Open Food Network." } - = t "admin_entreprise_groups_what_s_this" + = t "what_is_this" %div.seven.columns.alpha.list - if @product_count > 0 %div.seven.columns.alpha.list-item diff --git a/config/locales/en.yml b/config/locales/en.yml index 608bbd86ea..0a2904626d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -789,7 +789,7 @@ Please follow the instructions there to make your enterprise visible on the Open on_hand: "On hand" save_changes: "Save Changes" update_action: "update()" #FIXME - admin_entreprise_groups_what_s_this: "What's this ?" + what_is_this: "What's this?" spree_admin_overview_enterprises_header: "My Enterprises" spree_admin_overview_enterprises_footer: "MANAGE MY ENTERPRISES" spree_admin_enterprises_hubs_name: "Name" @@ -831,7 +831,6 @@ Please follow the instructions there to make your enterprise visible on the Open manage_products: "Manage products" edit_profile_details: "Edit profile details" edit_profile_details_etc: "Change your profile description, images, etc." - bulk_order_management: "Bulk Order Management" start_date: "Start Date" end_date: "End Date" order_cycle: "Order Cycle" From 5225686d8f4c4d40b61845a4950875b97569674a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Mar 2016 12:23:12 +1100 Subject: [PATCH 134/215] Re-merge bulk order view --- .../admin/shared/_columns_dropdown.html.haml | 4 ++- .../admin/orders/bulk_management.html.haml | 35 ++++++------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/app/views/admin/shared/_columns_dropdown.html.haml b/app/views/admin/shared/_columns_dropdown.html.haml index b16d388c1d..caab910ceb 100644 --- a/app/views/admin/shared/_columns_dropdown.html.haml +++ b/app/views/admin/shared/_columns_dropdown.html.haml @@ -1,6 +1,8 @@ %div.three.columns.omega %div.ofn-drop-down.right#columns-dropdown{ 'ng-controller' => "DropDownCtrl" } - %span{ :class => 'icon-reorder' }   Columns + %span{ :class => 'icon-reorder' } +   + = t "bom_columns" %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } %div.menu{ 'ng-show' => "expanded" } %div.menu_item.three.columns.alpha.omega{ 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index b0908e0b6e..9d6ca99586 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -87,29 +87,16 @@ %div{ :class => "eight columns alpha", 'ng-hide' => 'allFinalWeightVolumesPresent()' } %span{ :class => "eight columns alpha", style: 'color:red' } = t "bulk_management_warning" - %hr{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px" } - %div{ 'ng-hide' => 'RequestMonitor.loading || lineItems.length == 0' } - .controls{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px;" } - %div{ :class => "three columns alpha" } - %input{ :class => "fullwidth", :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } - %div{ :class => "three columns" } - %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "bulk_actions_dropdown", 'ofn-drop-down' => true } - %span{ :class => 'icon-check' } - = t "action" - %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } - %div.menu{ 'ng-show' => "expanded" } - %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "action in bulkActions", 'ng-click' => "$eval(action.callback)(filteredLineItems)", 'ofn-close-on-click' => true } - %span{ :class => 'three columns omega' } {{action.name }} - %div{ :class => "seven columns" }   - %div{ :class => "three columns omega" } - %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' } - %span{ :class => 'icon-reorder' } - = t "bom_columns" - %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } - %div.menu{ 'ng-show' => "expanded" } - %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } - %span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }} - %span{ :class => 'two columns omega' } {{column.name }} + + %hr.divider.sixteen.columns.alpha.omega + + .controls.sixteen.columns.alpha.omega{ ng: { hide: 'RequestMonitor.loading || lineItems.length == 0' } } + %div.three.columns.alpha + %input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } + = render 'admin/shared/bulk_actions_dropdown' + %div.seven.columns   + = render 'admin/shared/columns_dropdown' + %div.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' } %img.spinner{ src: "/assets/spinning-circles.svg" } %h1 @@ -119,7 +106,7 @@ %h1#no_results = t "bom_no_results" - %div{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } + .margin-bottom-50{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } %form{ name: 'bulk_order_form' } %table.index#listing_orders.bulk{ :class => "sixteen columns alpha" } %thead From b7320a0bd7bd3351db55b42fd95deea280d5780c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Mar 2016 12:48:35 +1100 Subject: [PATCH 135/215] Translate "Price" in Bulk Order Management --- app/assets/javascripts/admin/bulk_order_management.js.coffee | 2 +- .../line_items/controllers/line_items_controller.js.coffee | 2 +- config/locales/en.yml | 1 + spec/features/admin/bulk_order_management_spec.rb | 2 ++ 4 files changed, 5 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 c2250954b7..b747d3699e 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -30,7 +30,7 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ quantity: { name: t("bom_quantity"), visible: true } max: { name: t("bom_max"), visible: true } final_weight_volume: { name: t("bom_final_weigth_volume"), visible: false } - price: { name: t("bom_price"), visible: false } + price: { name: t("price"), visible: false } $scope.initialise = -> $scope.initialiseVariables() authorise_api_reponse = "" diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index d758706d65..61526dbea5 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -22,7 +22,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, quantity: { name: t("bom_quantity"), visible: true } max: { name: t("bom_max"), visible: true } final_weight_volume: { name: t("bom_final_weigth_volume"), visible: false } - price: { name: t("bom_price"), visible: false } + price: { name: t("price"), visible: false } $scope.confirmRefresh = -> LineItems.allSaved() || confirm(t "unsaved_changes_warning") diff --git a/config/locales/en.yml b/config/locales/en.yml index 0a2904626d..87dff559bc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -250,6 +250,7 @@ en: bom_hub: "Hub" bom_variant: "Product: Unit" bom_final_weigth_volume: "Weight/Volume" + bom_quantity: "Quantity" bom_actions_delete: "Delete Selected" bom_loading: "Loading orders" bom_no_results: "No orders found." diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index d28a20d6f9..8d1a8242e3 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -159,6 +159,8 @@ feature %q{ first("div#columns-dropdown", :text => "COLUMNS").click first("div#columns-dropdown div.menu div.menu_item", text: "Weight/Volume").click first("div#columns-dropdown div.menu div.menu_item", text: "Price").click + # hide dropdown + first("div#columns-dropdown", :text => "COLUMNS").click within "tr#li_#{li1.id}" do expect(page).to have_field "price", with: "$50.00" fill_in "final_weight_volume", :with => 2000 From 711f525d98856fbaa51657121e2a6cf9c6bd6b4e Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Mar 2016 14:45:43 +1100 Subject: [PATCH 136/215] Remove   from locale en.yml --- .../admin/dropdown/directives/links_dropdown.js.coffee | 5 ----- .../templates/admin/links_dropdown.html.haml | 10 ---------- app/assets/stylesheets/admin/orders.css.scss | 4 ++++ config/locales/en.yml | 3 +-- 4 files changed, 5 insertions(+), 17 deletions(-) delete mode 100644 app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee delete mode 100644 app/assets/javascripts/templates/admin/links_dropdown.html.haml diff --git a/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee b/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee deleted file mode 100644 index a58688a542..0000000000 --- a/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee +++ /dev/null @@ -1,5 +0,0 @@ - angular.module("admin.dropdown").directive "linksDropdown", ($window)-> - restrict: "C" - scope: - links: "=" - templateUrl: "admin/links_dropdown.html" diff --git a/app/assets/javascripts/templates/admin/links_dropdown.html.haml b/app/assets/javascripts/templates/admin/links_dropdown.html.haml deleted file mode 100644 index 1f44f2418c..0000000000 --- a/app/assets/javascripts/templates/admin/links_dropdown.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -.ofn-drop-down - %span - %i.icon-check - Actions - %i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } - %div.menu{ 'ng-show' => "expanded", style: 'width: 200px' } - %a.menu_item{ 'ng-repeat' => "link in links", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method || 'get' }}", confirm: "{{link.confirm}}" }, style: 'display: inline-block; width: 100%' } - %span{ :style => 'text-align: center; display: inline-block; width: 20%'} - %i{ ng: { class: "link.icon" } } - %span{ style: "display: inline-block; width: auto"} {{ link.name }} diff --git a/app/assets/stylesheets/admin/orders.css.scss b/app/assets/stylesheets/admin/orders.css.scss index 544abfa899..9a2dd4385c 100644 --- a/app/assets/stylesheets/admin/orders.css.scss +++ b/app/assets/stylesheets/admin/orders.css.scss @@ -70,3 +70,7 @@ div#group_buy_calculation { } } } + +th.actions { + white-space: nowrap; +} diff --git a/config/locales/en.yml b/config/locales/en.yml index 87dff559bc..9e056e2c25 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -841,8 +841,7 @@ Please follow the instructions there to make your enterprise visible on the Open current_fulfilled_units: "Current Fulfilled Units" max_fulfilled_units: "Max Fulfilled Units" bulk_management_warning: "WARNING: Some variants do not have a unit value" - action: "   Actions" - ask: "Ask? " + ask: "Ask?" no_orders_found: "No orders found." order_no: "Order No." weight_volume: "Weight/Volume" From ac88817a3409c7ad4de0c984cb056e1d13873229 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Mar 2016 15:46:29 +1100 Subject: [PATCH 137/215] Add translation for unsaved_changes_warning --- .../line_items/controllers/line_items_controller.js.coffee | 4 ++-- config/locales/en.yml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 61526dbea5..f905d33bfe 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -73,9 +73,9 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, StatusMessage.display 'success', "All changes saved" $scope.bulk_order_form.$setPristine() ).catch -> - StatusMessage.display 'failure', t "unsaved_changes_warning" + StatusMessage.display 'failure', t "unsaved_changes_error" else - StatusMessage.display 'failure', t "unsaved_changes_warning" + StatusMessage.display 'failure', t "unsaved_changes_error" $scope.deleteLineItem = (lineItem) -> if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete diff --git a/config/locales/en.yml b/config/locales/en.yml index 9a93d754ae..0bd24a9b54 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -302,7 +302,8 @@ en: bom_no_results: "No orders found." bom_order_error: "Some errors must be resolved before you can update orders.\nAny fields with red borders contain errors." - unsaved_changes_warning: "Fields with red borders contain errors." + unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." + unsaved_changes_error: "Fields with red borders contain errors." products: "Products" products_in: "in %{oc}" From 51f94119ff3350abebaa4a7af08afc44685ce977 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Mar 2016 16:39:07 +1100 Subject: [PATCH 138/215] Restore links_dropdown --- .../admin/dropdown/directives/links_dropdown.js.coffee | 5 +++++ .../templates/admin/links_dropdown.html.haml | 10 ++++++++++ 2 files changed, 15 insertions(+) create mode 100644 app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee create mode 100644 app/assets/javascripts/templates/admin/links_dropdown.html.haml diff --git a/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee b/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee new file mode 100644 index 0000000000..a58688a542 --- /dev/null +++ b/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee @@ -0,0 +1,5 @@ + angular.module("admin.dropdown").directive "linksDropdown", ($window)-> + restrict: "C" + scope: + links: "=" + templateUrl: "admin/links_dropdown.html" diff --git a/app/assets/javascripts/templates/admin/links_dropdown.html.haml b/app/assets/javascripts/templates/admin/links_dropdown.html.haml new file mode 100644 index 0000000000..1f44f2418c --- /dev/null +++ b/app/assets/javascripts/templates/admin/links_dropdown.html.haml @@ -0,0 +1,10 @@ +.ofn-drop-down + %span + %i.icon-check + Actions + %i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } + %div.menu{ 'ng-show' => "expanded", style: 'width: 200px' } + %a.menu_item{ 'ng-repeat' => "link in links", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method || 'get' }}", confirm: "{{link.confirm}}" }, style: 'display: inline-block; width: 100%' } + %span{ :style => 'text-align: center; display: inline-block; width: 20%'} + %i{ ng: { class: "link.icon" } } + %span{ style: "display: inline-block; width: auto"} {{ link.name }} From 2546603a671ea19138147fe5138987f6b3fcd611 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Mar 2016 17:08:13 +1100 Subject: [PATCH 139/215] Remove what_is_this from locale It got replaced by admin.whats_this. Changed views to use the new key. --- app/views/admin/enterprise_groups/_form_images.html.haml | 2 +- app/views/admin/enterprise_groups/_form_users.html.haml | 2 +- app/views/spree/admin/orders/bulk_management.html.haml | 2 +- app/views/spree/admin/overview/_order_cycles.html.haml | 2 +- app/views/spree/admin/overview/_products.html.haml | 2 +- config/locales/en.yml | 1 - 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/views/admin/enterprise_groups/_form_images.html.haml b/app/views/admin/enterprise_groups/_form_images.html.haml index 67378f331b..2269367e96 100644 --- a/app/views/admin/enterprise_groups/_form_images.html.haml +++ b/app/views/admin/enterprise_groups/_form_images.html.haml @@ -14,7 +14,7 @@ = f.label :promo_image, class: 'with-tip', 'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image) .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image_tip)} %a - = t 'what_is_this' + = t 'admin.whats_this' = f.label :promo_image, 'ofn-with-tip' => 'This image is displayed at the top of the Group profile' %div{'ofn-with-tip' => 'This image is displayed at the top of the Group profile'} %a What's this? diff --git a/app/views/admin/enterprise_groups/_form_users.html.haml b/app/views/admin/enterprise_groups/_form_users.html.haml index 082fa2d3bf..b09bde6852 100644 --- a/app/views/admin/enterprise_groups/_form_users.html.haml +++ b/app/views/admin/enterprise_groups/_form_users.html.haml @@ -5,7 +5,7 @@ =f.label :owner_id, t(:admin_entreprise_groups_owner) .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip)} %a - = t 'what_is_this' + = t 'admin.whats_this' =f.label :owner_id, 'Owner' %div{'ofn-with-tip' => "The primary user responsible for this group."} %a What's this? diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 9d6ca99586..3a962defac 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -5,7 +5,7 @@ %h1.page-title = t "bom_page_title" %a{ 'ofn-with-tip' => "#t('bom_tip')" } - = t "what_is_this" + = t "admin.whats_this" = render :partial => 'spree/admin/shared/order_sub_menu' diff --git a/app/views/spree/admin/overview/_order_cycles.html.haml b/app/views/spree/admin/overview/_order_cycles.html.haml index 9be7fffed2..8628837a47 100644 --- a/app/views/spree/admin/overview/_order_cycles.html.haml +++ b/app/views/spree/admin/overview/_order_cycles.html.haml @@ -7,7 +7,7 @@ = t "spree_admin_enterprises_create_new" - else %a.with-tip{ title: t(:spree_admin_order_cycles_tip) } - = t "what_is_this" + = t "admin.whats_this" %a{ "ofn-with-tip" => "Order cycles determine when and where your products are available to customers." } What's this? %div.seven.columns.alpha.list - if @order_cycle_count > 0 diff --git a/app/views/spree/admin/overview/_products.html.haml b/app/views/spree/admin/overview/_products.html.haml index 79da1a0f1b..c017cd11f9 100644 --- a/app/views/spree/admin/overview/_products.html.haml +++ b/app/views/spree/admin/overview/_products.html.haml @@ -7,7 +7,7 @@ = t "spree_admin_enterprises_create_new" - else %a{ "ofn-with-tip" => "The products that you sell through the Open Food Network." } - = t "what_is_this" + = t "admin.whats_this" %div.seven.columns.alpha.list - if @product_count > 0 %div.seven.columns.alpha.list-item diff --git a/config/locales/en.yml b/config/locales/en.yml index 0bd24a9b54..c4ea55c3c4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -835,7 +835,6 @@ Please follow the instructions there to make your enterprise visible on the Open on_hand: "On hand" save_changes: "Save Changes" update_action: "update()" #FIXME - what_is_this: "What's this?" spree_admin_overview_enterprises_header: "My Enterprises" spree_admin_overview_enterprises_footer: "MANAGE MY ENTERPRISES" spree_admin_enterprises_hubs_name: "Name" From e1fdd3a210ab2c83290222051e5cb268e9a64bba Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Fri, 11 Mar 2016 21:59:22 +0000 Subject: [PATCH 140/215] Apply style guide --- app/models/spree/user_decorator.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 7fb5d7ddb1..9684e86371 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -55,19 +55,21 @@ Spree.user_class.class_eval do end # Returns orders and their associated payments for all distributors that have been ordered from + def get_orders_by_distributor + Enterprise.includes(distributed_orders: { payments: :payment_method }) + .where(enterprises: { id: self.enterprises_ordered_from }, + spree_orders: { state: 'complete', user_id: self.id }) + .order('spree_orders.completed_at DESC') + end + def orders_by_distributor - data_array = Enterprise.includes(distributed_orders: {payments: :payment_method}) - .where(enterprises: { id: self.enterprises_ordered_from }, - spree_orders: { state: 'complete', user_id: self.id} - ).order('spree_orders.completed_at DESC') - .to_a # Remove uncompleted payments as these will not be reflected in order balance - data_array.each do |enterprise| + data_array = self.get_orders_by_distributor.to_a.each do |enterprise| enterprise.distributed_orders.each do |order| - order.payments.keep_if{ |payment| payment.state == "completed" } + order.payments.keep_if { |payment| payment.state == "completed" } end end - data_array.sort!{ |a, b| b.distributed_orders.length <=> a.distributed_orders.length } + data_array.sort! { |a, b| b.distributed_orders.length <=> a.distributed_orders.length } end private From e81ef564e321867c87041fe2a0270fac28f6d033 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Fri, 11 Mar 2016 22:01:09 +0000 Subject: [PATCH 141/215] Capitalize Spree state translations --- app/views/spree/users/_fat.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index 94088e2bef..f1aa38807e 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -14,8 +14,8 @@ %td.order1 %a{"bo-href" => "order.path", "bo-text" => "('order' | t )+ ' ' + order.number"} %td.order2{"bo-text" => "order.completed_at"} - %td.order3.show-for-large-up{"bo-text" => "'spree.payment_states.' + order.payment_state | t"} - %td.order4.show-for-large-up{"bo-text" => "'spree.shipment_states.' + order.shipment_state | t"} + %td.order3.show-for-large-up{"bo-text" => "'spree.payment_states.' + order.payment_state | t | capitalize"} + %td.order4.show-for-large-up{"bo-text" => "'spree.shipment_states.' + order.shipment_state | t | capitalize"} %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} %td.order6.text-right.show-for-large-up{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} From 6eb6a01339ae427c3a23cd260886f0f9ce8c0160 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 16 Mar 2016 10:45:22 +1100 Subject: [PATCH 142/215] Filter incomplete payments on database level Using explicit `LEFT OUTER JOIN ... ON ...` to query only completed payments for the order overview of consumers. They were filtered in Ruby before. --- app/models/spree/user_decorator.rb | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 9684e86371..a8177497b2 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -54,22 +54,10 @@ Spree.user_class.class_eval do self.orders.where(state: :complete).map(&:distributor_id).uniq end - # Returns orders and their associated payments for all distributors that have been ordered from - def get_orders_by_distributor - Enterprise.includes(distributed_orders: { payments: :payment_method }) - .where(enterprises: { id: self.enterprises_ordered_from }, - spree_orders: { state: 'complete', user_id: self.id }) - .order('spree_orders.completed_at DESC') - end - def orders_by_distributor - # Remove uncompleted payments as these will not be reflected in order balance - data_array = self.get_orders_by_distributor.to_a.each do |enterprise| - enterprise.distributed_orders.each do |order| - order.payments.keep_if { |payment| payment.state == "completed" } - end + distributors_with_orders.to_a.sort! do |a, b| + b.distributed_orders.length <=> a.distributed_orders.length end - data_array.sort! { |a, b| b.distributed_orders.length <=> a.distributed_orders.length } end private @@ -79,4 +67,15 @@ Spree.user_class.class_eval do errors.add(:owned_enterprises, "^#{email} is not permitted to own any more enterprises (limit is #{enterprise_limit}).") end end + + def distributors_with_orders + Enterprise + .select("DISTINCT enterprises.*") + .joins("LEFT OUTER JOIN spree_orders ON spree_orders.distributor_id = enterprises.id") + .joins("LEFT OUTER JOIN spree_payments ON spree_payments.order_id = spree_orders.id + AND spree_payments.state = 'completed'") + .joins("LEFT OUTER JOIN spree_payment_methods ON spree_payment_methods.id = spree_payments.payment_method_id") + .where(enterprises: { id: enterprises_ordered_from }, + spree_orders: { state: 'complete', user_id: id }) + end end From a48b992ec049eeb3fe3c980f8ebf36fcd0ad7346 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 18 Mar 2016 14:28:47 +1100 Subject: [PATCH 143/215] Shops can require users to login Enterprise users have a new option to restrict their shopfronts to logged in users only. If a guest visits one of these shopfornts, the guest is prompted to login and is not shown any products. Closes #849. --- app/helpers/shop_helper.rb | 4 ++++ .../form/_primary_details.html.haml | 17 +++++++++------ app/views/enterprises/shop.html.haml | 5 +++-- app/views/shop/_messages.html.haml | 9 +++++++- config/locales/en.yml | 14 +++++++++++++ ...6051131_add_require_login_to_enterprise.rb | 5 +++++ db/schema.rb | 3 ++- spec/features/admin/enterprises_spec.rb | 6 ++++++ .../consumer/shopping/shopping_spec.rb | 21 +++++++++++++++++++ 9 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20160316051131_add_require_login_to_enterprise.rb diff --git a/app/helpers/shop_helper.rb b/app/helpers/shop_helper.rb index 3066bfbe05..eb2643820f 100644 --- a/app/helpers/shop_helper.rb +++ b/app/helpers/shop_helper.rb @@ -7,4 +7,8 @@ module ShopHelper ] end end + + def require_login? + current_distributor.require_login? && spree_current_user.nil? + end end diff --git a/app/views/admin/enterprises/form/_primary_details.html.haml b/app/views/admin/enterprises/form/_primary_details.html.haml index 437a61e866..ccf59c8a12 100644 --- a/app/views/admin/enterprises/form/_primary_details.html.haml +++ b/app/views/admin/enterprises/form/_primary_details.html.haml @@ -22,7 +22,6 @@ %a What's this? .five.columns.omega = f.check_box :is_primary_producer, 'ng-model' => 'Enterprise.is_primary_producer' -   = f.label :is_primary_producer, 'Producer' - if spree_current_user.admin? .row @@ -33,15 +32,12 @@ %a What's this? .two.columns = f.radio_button :sells, "none", 'ng-model' => 'Enterprise.sells' -   = f.label :sells, "None", value: "none" .two.columns = f.radio_button :sells, "own", 'ng-model' => 'Enterprise.sells' -   = f.label :sells, "Own", value: "own" .four.columns.omega = f.radio_button :sells, "any", 'ng-model' => 'Enterprise.sells' -   = f.label :sells, "Any", value: "any" .row .three.columns.alpha @@ -50,12 +46,21 @@ %a What's this? .two.columns = f.radio_button :visible, true -   = f.label :visible, "Visible", :value => "true" .five.columns.omega = f.radio_button :visible, false -   = f.label :visible, "Not Visible", :value => "false" +.row + .three.columns.alpha + %label= t '.shopfront_requires_login' + %div{'ofn-with-tip' => t('.shopfront_requires_login_tip')} + %a= t 'admin.whats_this' + .two.columns + = f.radio_button :require_login, false + = f.label :require_login, t('.shopfront_requires_login_false'), value: :false + .five.columns.omega + = f.radio_button :require_login, true + = f.label :require_login, t('.shopfront_requires_login_true'), value: :true .permalink{ ng: { controller: "permalinkCtrl" } } .row{ ng: { show: "Enterprise.sells == 'own' || Enterprise.sells == 'any'" } } .three.columns.alpha diff --git a/app/views/enterprises/shop.html.haml b/app/views/enterprises/shop.html.haml index f2063494b4..980cb2b3b0 100644 --- a/app/views/enterprises/shop.html.haml +++ b/app/views/enterprises/shop.html.haml @@ -22,6 +22,7 @@ %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id", "ofn-change-order-cycle" => true, + "disabled" => require_login?, "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}", "popover-placement" => "left", "popover" => t(:enterprises_choose), "popover-trigger" => "openTrigger"} @@ -31,7 +32,7 @@ = render partial: 'shop/messages' - .row - = render partial: "shop/products/form" + - unless require_login? + .row= render partial: "shop/products/form" = render partial: "shared/footer" diff --git a/app/views/shop/_messages.html.haml b/app/views/shop/_messages.html.haml index 1e56aba468..3082d05c12 100644 --- a/app/views/shop/_messages.html.haml +++ b/app/views/shop/_messages.html.haml @@ -1,5 +1,12 @@ -- if @order_cycles and @order_cycles.empty? +- if require_login? + .row.footer-pad + .small-12.columns + .shopfront_closed_message + = t '.require_login_html', + {login: link_to(t('.login'), login_path), + register: link_to(t('.register'), '/register')} +- elsif @order_cycles and @order_cycles.empty? - if current_distributor.preferred_shopfront_closed_message.present? .row .small-12.columns diff --git a/config/locales/en.yml b/config/locales/en.yml index 323288edc2..6db986d7b5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -105,6 +105,20 @@ en: enterprise: select_outgoing_oc_products_from: Select outgoing OC products from + enterprises: + form: + primary_details: + shopfront_requires_login: "Shopfront requires login?" + shopfront_requires_login_tip: "Choose whether viewing the shopfront requires to login or not." + shopfront_requires_login_false: "Public" + shopfront_requires_login_true: "Require customers to login" + + shop: + messages: + login: "login" + register: "register" + require_login_html: "Please %{login} if you have an account already. Otherwise, %{register} to become a customer." + # Printable Invoice Columns invoice_column_item: "Item" invoice_column_qty: "Qty" diff --git a/db/migrate/20160316051131_add_require_login_to_enterprise.rb b/db/migrate/20160316051131_add_require_login_to_enterprise.rb new file mode 100644 index 0000000000..68de642b62 --- /dev/null +++ b/db/migrate/20160316051131_add_require_login_to_enterprise.rb @@ -0,0 +1,5 @@ +class AddRequireLoginToEnterprise < ActiveRecord::Migration + def change + add_column :enterprises, :require_login, :boolean, default: false, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index cdab93834c..9994617a5f 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 => 20160302044850) do +ActiveRecord::Schema.define(:version => 20160316051131) do create_table "account_invoices", :force => true do |t| t.integer "user_id", :null => false @@ -348,6 +348,7 @@ ActiveRecord::Schema.define(:version => 20160302044850) do t.string "permalink", :null => false t.boolean "charges_sales_tax", :default => false, :null => false t.string "email_address" + t.boolean "require_login", :default => false, :null => false end add_index "enterprises", ["address_id"], :name => "index_enterprises_on_address_id" diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 92ac89015e..b72258f7f9 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -82,6 +82,10 @@ feature %q{ page.should have_selector '.available' choose 'Own' + # Require login to view shopfront + expect(page).to have_checked_field "enterprise_require_login_false" + choose "Require customers to login" + within (".side_menu") { click_link "Users" } select2_search user.email, from: 'Owner' @@ -162,6 +166,8 @@ feature %q{ page.should have_field 'enterprise_name', :with => 'Eaterprises' @enterprise.reload expect(@enterprise.owner).to eq user + expect(page).to have_checked_field "enterprise_visible_true" + expect(page).to have_checked_field "enterprise_require_login_true" click_link "Business Details" page.should have_checked_field "enterprise_charges_sales_tax_true" diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index e81a792881..5b8bb5881f 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -253,5 +253,26 @@ feature "As a consumer I want to shop with a distributor", js: true do page.should have_content "The next cycle opens in 10 days" end end + + context "when shopping requires to login" do + let(:exchange) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } + let(:product) { create(:simple_product) } + let(:variant) { create(:variant, product: product) } + + before do + add_product_and_variant_to_order_cycle(exchange, product, variant) + set_order_cycle(order, oc1) + distributor.require_login = true + distributor.save! + visit shop_path + end + + it "tells us to login" do + expect(page).to have_content "Please login" + end + it "does not show products" do + expect(page).to have_no_content product.name + end + end end end From 2a9e35355eb1b3f081b6124b1e72ecc10300b5b9 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 18 Mar 2016 16:13:20 +1100 Subject: [PATCH 144/215] Add `auth` directive to use AuthenticationService The auth directive binds to all elements with the auth attribute. It adds a click event that opens the login/register/password modal. --- app/assets/javascripts/darkswarm/directives/auth.js.coffee | 5 +++++ app/views/shop/_messages.html.haml | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/directives/auth.js.coffee diff --git a/app/assets/javascripts/darkswarm/directives/auth.js.coffee b/app/assets/javascripts/darkswarm/directives/auth.js.coffee new file mode 100644 index 0000000000..46ae301651 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/auth.js.coffee @@ -0,0 +1,5 @@ +Darkswarm.directive 'auth', (AuthenticationService) -> + restrict: 'A' + link: (scope, elem, attrs) -> + elem.bind "click", -> + AuthenticationService.open '/' + attrs.auth diff --git a/app/views/shop/_messages.html.haml b/app/views/shop/_messages.html.haml index 3082d05c12..5d98d142c5 100644 --- a/app/views/shop/_messages.html.haml +++ b/app/views/shop/_messages.html.haml @@ -4,8 +4,8 @@ .small-12.columns .shopfront_closed_message = t '.require_login_html', - {login: link_to(t('.login'), login_path), - register: link_to(t('.register'), '/register')} + {login: ('' + t('.login') + '').html_safe, + register: ('' + t('.register') + '').html_safe} - elsif @order_cycles and @order_cycles.empty? - if current_distributor.preferred_shopfront_closed_message.present? .row From 41970ecf077bbffca1a9488ff9ade9b503003796 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 18 Mar 2016 16:22:41 +1100 Subject: [PATCH 145/215] Use auth directive for Login button Use the auth directive instead of the AuthenticationCtrl for the Login button in the menu and on checkout. --- app/views/checkout/_authentication.html.haml | 6 +++--- app/views/shared/_signed_out.html.haml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/checkout/_authentication.html.haml b/app/views/checkout/_authentication.html.haml index 2120ba6549..1c3b06e63b 100644 --- a/app/views/checkout/_authentication.html.haml +++ b/app/views/checkout/_authentication.html.haml @@ -1,11 +1,11 @@ %section{"ng-show" => "!enabled"} .row - .small-12.columns.text-center{"ng-controller" => "AuthenticationCtrl"} + .small-12.columns.text-center %h3.pad-top = t :checkout_headline .row.pad-top - .small-5.columns.text-center{"ng-controller" => "AuthenticationCtrl"} - %button.primary.expand{"ng-click" => "open()"} + .small-5.columns.text-center + %button.primary.expand{"auth" => "login"} = t :label_login .small-2.columns.text-center %p.pad-top= "-#{t :action_or}-" diff --git a/app/views/shared/_signed_out.html.haml b/app/views/shared/_signed_out.html.haml index 1a7cbcc920..1e6efdc32a 100644 --- a/app/views/shared/_signed_out.html.haml +++ b/app/views/shared/_signed_out.html.haml @@ -1,5 +1,5 @@ -%li#login-link{"ng-controller" => "AuthenticationCtrl"} - %a{"ng-click" => "open()"} +%li#login-link + %a{"auth" => "login"} %i.ofn-i_017-locked %span = t 'label_login' From 276d3026c824d81d643ecb63109cc6bcadc05b10 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Fri, 18 Mar 2016 20:05:52 +0000 Subject: [PATCH 146/215] Adding specs for orders by ditributor query --- spec/models/spree/user_spec.rb | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index 7fefd56afb..ee01326316 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -79,4 +79,43 @@ describe Spree.user_class do end end end + + describe "retrieving orders for /account page" do + let!(:u1) { create(:user) } + let!(:u2) { create(:user) } + let!(:distributor1) { create(:distributor_enterprise) } + let!(:distributor2) { create(:distributor_enterprise) } + let!(:d1o1) { create(:completed_order_with_totals, distributor: distributor1, user_id: u1.id)} + let!(:d1o2) { create(:completed_order_with_totals, distributor: distributor1, user_id: u1.id)} + let!(:d1_order_for_u2) { create(:completed_order_with_totals, distributor: distributor1, user_id: u2.id)} + let!(:d1o3) { create(:order, state: 'cart', distributor: distributor1, user_id: u1.id)} + let!(:d2o1) { create(:completed_order_with_totals, distributor: distributor2, user_id: u2.id)} + + let!(:completed_payment) { create(:payment, order: d1o1, state: 'completed')} + let!(:payment) { create(:payment, order: d1o2, state: 'invalid')} + + it "returns enterprises that the user has ordered from" do + expect(u1.enterprises_ordered_from).to eq [distributor1.id] + end + + it "returns orders and payments for the user, organised by distributor" do + expect(u1.orders_by_distributor).to include distributor1 + expect(u1.orders_by_distributor.first.distributed_orders).to include d1o1 + end + + it "doesn't return irrelevant distributors" do + expect(u1.orders_by_distributor).not_to include distributor2 + end + it "doesn't return other users' orders" do + expect(u1.orders_by_distributor.first.distributed_orders).not_to include d1_order_for_u2 + end + + it "doesn't return uncompleted orders" do + expect(u1.orders_by_distributor.first.distributed_orders).not_to include d1o3 + end + + it "doesn't return uncompleted payments" do + expect(u1.orders_by_distributor.first.distributed_orders.map{|o| o.payments}.flatten).not_to include payment + end + end end From 6d0dc438a7898153bb630c5f0c03b5b4173350b2 Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Mon, 21 Mar 2016 16:55:57 -0600 Subject: [PATCH 147/215] i18n fixes after @sstead feedback in #799 --- .../edit.html.haml | 4 ++-- app/views/admin/customers/index.html.haml | 2 +- .../enterprise_groups/_form_images.html.haml | 3 --- .../enterprise_groups/_form_users.html.haml | 6 ++---- app/views/admin/order_cycles/index.html.haml | 21 ++++++++++++------- .../admin/orders/bulk_management.html.haml | 2 +- .../spree/admin/reports/customers.html.haml | 9 ++++---- .../reports/order_cycle_management.html.haml | 2 +- .../reports/orders_and_fulfillment.html.haml | 2 +- .../reports/products_and_inventory.html.haml | 6 +++--- .../admin/shared/_hubs_sidebar.html.haml | 7 ++++--- config/locales/en.yml | 6 +++--- 12 files changed, 36 insertions(+), 34 deletions(-) diff --git a/app/views/admin/accounts_and_billing_settings/edit.html.haml b/app/views/admin/accounts_and_billing_settings/edit.html.haml index cd614b14f8..2121719e75 100644 --- a/app/views/admin/accounts_and_billing_settings/edit.html.haml +++ b/app/views/admin/accounts_and_billing_settings/edit.html.haml @@ -28,7 +28,7 @@ =t :update_invoice = f.check_box :auto_update_invoices = f.label :auto_update_invoices, - t(:auto_finalise_invoices) + t(:auto_update_invoices) .six.columns.omega %fieldset.no-border-bottom @@ -36,7 +36,7 @@ =t :finalise_invoice = f.check_box :auto_finalize_invoices = f.label :auto_finalize_invoices, - t(:auto_update_invoices) + t(:auto_finalise_invoices) .row .twelve.columns.alpha.omega.form-buttons{"data-hook" => "buttons"} diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index cf3ef25bb3..f68ad37da1 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -1,6 +1,6 @@ - content_for :page_title do %h1.page-title - =t :customers + =t :customers = admin_inject_shops diff --git a/app/views/admin/enterprise_groups/_form_images.html.haml b/app/views/admin/enterprise_groups/_form_images.html.haml index 2269367e96..ba40a4c849 100644 --- a/app/views/admin/enterprise_groups/_form_images.html.haml +++ b/app/views/admin/enterprise_groups/_form_images.html.haml @@ -15,9 +15,6 @@ .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image_tip)} %a = t 'admin.whats_this' - = f.label :promo_image, 'ofn-with-tip' => 'This image is displayed at the top of the Group profile' - %div{'ofn-with-tip' => 'This image is displayed at the top of the Group profile'} - %a What's this? .omega.eight.columns = image_tag @object.promo_image.url if @object.promo_image.present? = f.file_field :promo_image diff --git a/app/views/admin/enterprise_groups/_form_users.html.haml b/app/views/admin/enterprise_groups/_form_users.html.haml index b09bde6852..6bd6eaa4a3 100644 --- a/app/views/admin/enterprise_groups/_form_users.html.haml +++ b/app/views/admin/enterprise_groups/_form_users.html.haml @@ -1,14 +1,12 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Users'" } } - %legend Users + %legend + = t(:users) .row .three.columns.alpha =f.label :owner_id, t(:admin_entreprise_groups_owner) .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip)} %a = t 'admin.whats_this' - =f.label :owner_id, 'Owner' - %div{'ofn-with-tip' => "The primary user responsible for this group."} - %a What's this? .eight.columns.omega - if spree_current_user.admin? = f.hidden_field :owner_id, diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index 10b9b1e345..824aa66ee8 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -28,14 +28,21 @@ %thead %tr - %th =t :name - %th =t :open - %th =t :close + %th + =t :name + %th + =t :open + %th + =t :close - unless order_cycles_simple_index - %th =t :supplier - %th =t :coordinator - %th =t :distributors - %th =t :products + %th + =t :supplier + %th + =t :coordinator + %th + =t :distributors + %th + =t :products %th.actions %th.actions %th.actions diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 3a962defac..43983e87ea 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -4,7 +4,7 @@ - content_for :page_title do %h1.page-title = t "bom_page_title" - %a{ 'ofn-with-tip' => "#t('bom_tip')" } + %a{ 'ofn-with-tip' => t("bom_tip") } = t "admin.whats_this" = render :partial => 'spree/admin/shared/order_sub_menu' diff --git a/app/views/spree/admin/reports/customers.html.haml b/app/views/spree/admin/reports/customers.html.haml index 4b7004536c..70045662f9 100644 --- a/app/views/spree/admin/reports/customers.html.haml +++ b/app/views/spree/admin/reports/customers.html.haml @@ -2,24 +2,24 @@ %br .row .four.columns.alpha - = label_tag nil, t(:reports_customers_distributor) + = label_tag nil, t(:report_customers_distributor) = select_tag(:distributor_id, options_from_collection_for_select(@distributors, :id, :name, params[:distributor_id]), {:include_blank => true, :class => "select2 fullwidth"}) .four.columns - = label_tag nil, t(:reports_customers_supplier) + = label_tag nil, t(:report_customers_supplier) = select_tag(:supplier_id, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id]), {:include_blank => true, :class => "select2 fullwidth"}) .six.columns - = label_tag nil, t(:reports_customers_cycle) + = label_tag nil, t(:report_customers_cycle) = select_tag(:order_cycle_id, options_for_select(report_order_cycle_options(@order_cycles), params[:order_cycle_id]), {:include_blank => true, :class => "select2 fullwidth"}) - = label_tag nil, t(:reports_customers_type) + = label_tag nil, t(:report_customers_type) = select_tag(:report_type, options_for_select(@report_types, @report_type)) %br @@ -44,4 +44,3 @@ - if @report.table.empty? %tr %td{:colspan => "2"}= t(:none) - 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 09ac6f76bb..0b32eabe78 100644 --- a/app/views/spree/admin/reports/order_cycle_management.html.haml +++ b/app/views/spree/admin/reports/order_cycle_management.html.haml @@ -6,7 +6,7 @@ .omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, t(:reports_customers_cycle) + .alpha.two.columns= label_tag nil, t(:report_customers_cycle) .omega.fourteen.columns = f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true}) diff --git a/app/views/spree/admin/reports/orders_and_fulfillment.html.haml b/app/views/spree/admin/reports/orders_and_fulfillment.html.haml index 15ad6458cd..5bda882108 100644 --- a/app/views/spree/admin/reports/orders_and_fulfillment.html.haml +++ b/app/views/spree/admin/reports/orders_and_fulfillment.html.haml @@ -10,7 +10,7 @@ .omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true}) .row - .alpha.two.columns= label_tag nil, t(:reports_customers_cycle) + .alpha.two.columns= label_tag nil, t(:report_customers_cycle) .omega.fourteen.columns = f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true}) diff --git a/app/views/spree/admin/reports/products_and_inventory.html.haml b/app/views/spree/admin/reports/products_and_inventory.html.haml index b1c11918ce..40c4fbfccf 100644 --- a/app/views/spree/admin/reports/products_and_inventory.html.haml +++ b/app/views/spree/admin/reports/products_and_inventory.html.haml @@ -7,14 +7,14 @@ options_from_collection_for_select(@distributors, :id, :name, params[:distributor_id]), {:include_blank => true, :class => "select2 fullwidth"}) - + .four.columns - = label_tag nil, t(:reports_customers_supplier) + = label_tag nil, t(:report_customers_supplier) = select_tag(:supplier_id, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id]), {:include_blank => true, :class => "select2 fullwidth"}) - + .six.columns = label_tag nil, t(:report_order_cycle) = select_tag(:order_cycle_id, diff --git a/app/views/spree/admin/shared/_hubs_sidebar.html.haml b/app/views/spree/admin/shared/_hubs_sidebar.html.haml index 9bc54be829..75ef9bb32f 100644 --- a/app/views/spree/admin/shared/_hubs_sidebar.html.haml +++ b/app/views/spree/admin/shared/_hubs_sidebar.html.haml @@ -2,7 +2,8 @@ - hubs_color = 'red' if (controller.action_name == 'create' || controller.action_name == 'update') && @object.errors.full_messages.include?(t(:hub_sidebar_at_least)) .sidebar_item.omega.four.columns#hubs .four.columns.alpha.header{ class: "#{hubs_color}" } - %span.four.columns.alpha.centered t(:hub_sidebar_hubs) + %span.four.columns.alpha.centered + = t(:hub_sidebar_hubs) .four.columns.alpha.list{ class: "#{hubs_color}" } - if @hubs.count > 0 = hidden_field klass, :distributor_ids, :multiple => true, value: nil @@ -18,9 +19,9 @@ - else .four.columns.alpha.list-item %span.three.columns.alpha - t(:hub_sidebar_none_available) + = t(:hub_sidebar_none_available) %span.one.column.omega %span.icon-remove-sign %a.four.columns.alpha.button{ href: "#{main_app.admin_enterprises_path}", class: "#{hubs_color}" } - t(:hub_sidebar_manage) + = t(:hub_sidebar_manage) %span.icon-arrow-right diff --git a/config/locales/en.yml b/config/locales/en.yml index c4ea55c3c4..477c83cc5d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -906,9 +906,9 @@ Please follow the instructions there to make your enterprise visible on the Open hub_sidebar_red: "red" shop_trial_in_progress: "Your shopfront trial expires in #{remaining_trial_days(enterprise)}." #FIXME shop_trial_expired: "Good news! We have decided to extend shopfront trials until further notice (probably around March 2015)." #FIXME - reports_customers_distributor: "Distributor" - reports_customers_supplier: "Supplier" - reports_customers_cycle: "Order Cycle" + report_customers_distributor: "Distributor" + report_customers_supplier: "Supplier" + report_customers_cycle: "Order Cycle" report_customers_type: "Report Type" report_customers_csv: "Download as csv" report_customers_header: "orders header" From 79ad05d405bdd559c09145c0e3e191aa36a506e3 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 23 Mar 2016 10:01:04 +1100 Subject: [PATCH 148/215] i18n group image help text --- .../admin/enterprise_groups/_form_images.html.haml | 13 ++++++------- config/locales/en.yml | 3 +-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/views/admin/enterprise_groups/_form_images.html.haml b/app/views/admin/enterprise_groups/_form_images.html.haml index ba40a4c849..24bfad3bf0 100644 --- a/app/views/admin/enterprise_groups/_form_images.html.haml +++ b/app/views/admin/enterprise_groups/_form_images.html.haml @@ -3,18 +3,17 @@ = t 'admin_entreprise_groups_images' .row .alpha.three.columns - = f.label :logo, 'ofn-with-tip' => 'This is the logo for the group' - %div{'ofn-with-tip' => 'This is the logo for the group'} - %a What's this? + = f.label :logo, 'ofn-with-tip' => t('admin_entreprise_groups_data_powertip_logo') + %div{'ofn-with-tip' => t('admin_entreprise_groups_data_powertip_logo')} + %a= t 'admin.whats_this' .omega.eight.columns = image_tag @object.logo.url if @object.logo.present? = f.file_field :logo .row .alpha.three.columns - = f.label :promo_image, class: 'with-tip', 'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image) - .with-tip{'data-powertip' => t(:admin_entreprise_groups_data_powertip_promo_image_tip)} - %a - = t 'admin.whats_this' + = f.label :promo_image, 'ofn-with-tip' => t(:admin_entreprise_groups_data_powertip_promo_image) + %div{'ofn-with-tip' => t('admin_entreprise_groups_data_powertip_promo_image')} + %a= t 'admin.whats_this' .omega.eight.columns = image_tag @object.promo_image.url if @object.promo_image.present? = f.file_field :promo_image diff --git a/config/locales/en.yml b/config/locales/en.yml index 477c83cc5d..da0948dece 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -772,9 +772,8 @@ Please follow the instructions there to make your enterprise visible on the Open admin_entreprise_groups_entreprise: "Enterprises" admin_entreprise_groups_primary_details: "Primary Details" admin_entreprise_groups_data_powertip: "The primary user responsible for this group." - admin_entreprise_groups_data_powertip_logo: "This is the logo" + admin_entreprise_groups_data_powertip_logo: "This is the logo for the group" admin_entreprise_groups_data_powertip_promo_image: "This image is displayed at the top of the Group profile" - admin_entreprise_groups_data_powertip_promo_image_tip: "This image is displayed at the top of the Group profile" admin_entreprise_groups_about: "About" admin_entreprise_groups_images: "Images" admin_entreprise_groups_contact: "Contact" From 31302fb93013894d07eb6d45f17741db9f45803c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 23 Mar 2016 17:57:22 +1100 Subject: [PATCH 149/215] Translate unsaved products message correctly --- .../javascripts/admin/bulk_product_update.js.coffee | 10 +++++----- config/locales/en.yml | 5 ++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index cb337c4c47..3d565f703a 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -265,11 +265,11 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.displayDirtyProducts = -> - if DirtyProducts.count() > 0 - message = if DirtyProducts.count() == 1 then t("one_product") else DirtyProducts.count() + t("products") - StatusMessage.display 'notice', t("changes_to") + "#{message}" + t("remain_unsaved.") - else - StatusMessage.clear() + count = DirtyProducts.count() + switch count + when 0 then StatusMessage.clear() + when 1 then StatusMessage.display 'notice', t("one_product_unsaved") + else StatusMessage.display 'notice', t("products_unsaved", n: count) filterSubmitProducts = (productsToFilter) -> diff --git a/config/locales/en.yml b/config/locales/en.yml index da0948dece..2656fe91c9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -938,9 +938,8 @@ Please follow the instructions there to make your enterprise visible on the Open success: "success" failure: "failure" unsaved_changes_confirmation: "Unsaved changes will be lost. Continue anyway?" - one_product: "one product" - changes_to: "Changes to" - remain_unsaved: "remain unsaced" + one_product_unsaved: "Changes to one product remain unsaved." + products_unsaved: "Changes to %{n} products remain unsaved." add_manager: "Add a manager" is_already_manager: "is already a manager!" no_change_to_save: " No change to save" From df36386757f1f15dfcccf490f5769e3510b694bb Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 24 Mar 2016 18:24:46 +1100 Subject: [PATCH 150/215] Require customer instead of user If a shop requires a login, then a customer needs to be logged in, not just any user. --- app/helpers/shop_helper.rb | 4 +- app/views/enterprises/shop.html.haml | 4 +- app/views/shop/_messages.html.haml | 14 +++++-- config/locales/en.yml | 3 ++ .../consumer/shopping/shopping_spec.rb | 41 ++++++++++++++++--- 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/app/helpers/shop_helper.rb b/app/helpers/shop_helper.rb index eb2643820f..ed2facd86f 100644 --- a/app/helpers/shop_helper.rb +++ b/app/helpers/shop_helper.rb @@ -8,7 +8,7 @@ module ShopHelper end end - def require_login? - current_distributor.require_login? && spree_current_user.nil? + def require_customer? + current_distributor.require_login? and not spree_current_user.andand.customer_of current_distributor end end diff --git a/app/views/enterprises/shop.html.haml b/app/views/enterprises/shop.html.haml index 980cb2b3b0..d635f68164 100644 --- a/app/views/enterprises/shop.html.haml +++ b/app/views/enterprises/shop.html.haml @@ -22,7 +22,7 @@ %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id", "ofn-change-order-cycle" => true, - "disabled" => require_login?, + "disabled" => require_customer?, "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}", "popover-placement" => "left", "popover" => t(:enterprises_choose), "popover-trigger" => "openTrigger"} @@ -32,7 +32,7 @@ = render partial: 'shop/messages' - - unless require_login? + - unless require_customer? .row= render partial: "shop/products/form" = render partial: "shared/footer" diff --git a/app/views/shop/_messages.html.haml b/app/views/shop/_messages.html.haml index 5d98d142c5..7f5451334b 100644 --- a/app/views/shop/_messages.html.haml +++ b/app/views/shop/_messages.html.haml @@ -1,11 +1,17 @@ -- if require_login? +- if require_customer? .row.footer-pad .small-12.columns .shopfront_closed_message - = t '.require_login_html', - {login: ('' + t('.login') + '').html_safe, - register: ('' + t('.register') + '').html_safe} + = t '.require_customer_login' + - if spree_current_user.nil? + = t '.require_login_html', + {login: ('' + t('.login') + '').html_safe, + register: ('' + t('.register') + '').html_safe} + - else + = t '.require_customer_html', + {contact: ('' + t('.contact') + '').html_safe, + enterprise: current_distributor.name} - elsif @order_cycles and @order_cycles.empty? - if current_distributor.preferred_shopfront_closed_message.present? .row diff --git a/config/locales/en.yml b/config/locales/en.yml index 6db986d7b5..40abae6dd2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -117,7 +117,10 @@ en: messages: login: "login" register: "register" + contact: "contact" + require_customer_login: "This shop is for customers only." require_login_html: "Please %{login} if you have an account already. Otherwise, %{register} to become a customer." + require_customer_html: "Please %{contact} %{enterprise} to become a customer." # Printable Invoice Columns invoice_column_item: "Item" diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 5b8bb5881f..fe706c24ab 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -254,7 +254,7 @@ feature "As a consumer I want to shop with a distributor", js: true do end end - context "when shopping requires to login" do + context "when shopping requires a customer" do let(:exchange) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } let(:product) { create(:simple_product) } let(:variant) { create(:variant, product: product) } @@ -264,14 +264,43 @@ feature "As a consumer I want to shop with a distributor", js: true do set_order_cycle(order, oc1) distributor.require_login = true distributor.save! - visit shop_path end - it "tells us to login" do - expect(page).to have_content "Please login" + context "when not logged in" do + it "tells us to login" do + visit shop_path + expect(page).to have_content "This shop is for customers only." + expect(page).to have_content "Please login" + expect(page).to have_no_content product.name + end end - it "does not show products" do - expect(page).to have_no_content product.name + + context "when logged in" do + let(:address) { create(:address, firstname: "Foo", lastname: "Bar") } + let(:user) { create(:user, bill_address: address, ship_address: address) } + + before do + quick_login_as user + end + + context "as non-customer" do + it "tells us to contact enterprise" do + visit shop_path + expect(page).to have_content "This shop is for customers only." + expect(page).to have_content "Please contact #{distributor.name}" + expect(page).to have_no_content product.name + end + end + + context "as customer" do + let!(:customer) { create(:customer, user: user, enterprise: distributor) } + + it "shows just products" do + visit shop_path + expect(page).to have_no_content "This shop is for customers only." + expect(page).to have_content product.name + end + end end end end From 5149c5118b2fd98d1859c8a03c0b7cdb1fd1b852 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 24 Mar 2016 18:40:38 +1100 Subject: [PATCH 151/215] Change text and style for shops requiring login --- app/assets/stylesheets/darkswarm/shop.css.sass | 8 +++++++- app/helpers/shop_helper.rb | 4 +++- app/views/shop/_messages.html.haml | 2 +- config/locales/en.yml | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 5422c4463a..eab074369e 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -84,9 +84,11 @@ padding-right: 0rem font-size: 0.8rem - .shopfront_message, .shopfront_closed_message + .shopfront_message, .shopfront_closed_message, .shopfront_hidden_message padding: 15px border-radius: 5px + + .shopfront_message, .shopfront_closed_message border: 2px solid #eb4c46 .shopfront_message @@ -94,3 +96,7 @@ .shopfront_closed_message margin: 2em 0em + + .shopfront_hidden_message + border: 2px solid #db4 + margin: 2em 0em diff --git a/app/helpers/shop_helper.rb b/app/helpers/shop_helper.rb index ed2facd86f..43c1583bbb 100644 --- a/app/helpers/shop_helper.rb +++ b/app/helpers/shop_helper.rb @@ -9,6 +9,8 @@ module ShopHelper end def require_customer? - current_distributor.require_login? and not spree_current_user.andand.customer_of current_distributor + current_distributor.require_login? && !( + spree_current_user.andand.customer_of current_distributor + ) end end diff --git a/app/views/shop/_messages.html.haml b/app/views/shop/_messages.html.haml index 7f5451334b..ba35354bf3 100644 --- a/app/views/shop/_messages.html.haml +++ b/app/views/shop/_messages.html.haml @@ -2,7 +2,7 @@ - if require_customer? .row.footer-pad .small-12.columns - .shopfront_closed_message + .shopfront_hidden_message = t '.require_customer_login' - if spree_current_user.nil? = t '.require_login_html', diff --git a/config/locales/en.yml b/config/locales/en.yml index 40abae6dd2..95c4e0b92a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -109,7 +109,7 @@ en: form: primary_details: shopfront_requires_login: "Shopfront requires login?" - shopfront_requires_login_tip: "Choose whether viewing the shopfront requires to login or not." + shopfront_requires_login_tip: "Choose whether customers must login to view the shopfront." shopfront_requires_login_false: "Public" shopfront_requires_login_true: "Require customers to login" From c4f499d5186ac73b7482b1e0ee44247c7a791ed7 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 25 Mar 2016 12:04:26 +1100 Subject: [PATCH 152/215] Allow admins and managers to see restricted shops --- app/helpers/shop_helper.rb | 10 ++++++-- .../consumer/shopping/shopping_spec.rb | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/app/helpers/shop_helper.rb b/app/helpers/shop_helper.rb index 43c1583bbb..920d41d65e 100644 --- a/app/helpers/shop_helper.rb +++ b/app/helpers/shop_helper.rb @@ -9,8 +9,14 @@ module ShopHelper end def require_customer? - current_distributor.require_login? && !( - spree_current_user.andand.customer_of current_distributor + current_distributor.require_login? && !user_is_related_to_distributor? + end + + def user_is_related_to_distributor? + spree_current_user.present? && ( + spree_current_user.admin? || + spree_current_user.enterprises.include?(current_distributor) || + spree_current_user.customer_of(current_distributor) ) end end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index fe706c24ab..a8545af3db 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -301,6 +301,29 @@ feature "As a consumer I want to shop with a distributor", js: true do expect(page).to have_content product.name end end + + context "as a manager" do + let!(:role) { create(:enterprise_role, user: user, enterprise: distributor) } + + it "shows just products" do + visit shop_path + expect(page).to have_no_content "This shop is for customers only." + expect(page).to have_content product.name + end + end + + context "as the owner" do + before do + distributor.owner = user + distributor.save! + end + + it "shows just products" do + visit shop_path + expect(page).to have_no_content "This shop is for customers only." + expect(page).to have_content product.name + end + end end end end From 5a6caa294ebe7fc5ec8fb177791505a3f27fd962 Mon Sep 17 00:00:00 2001 From: stveep Date: Mon, 28 Mar 2016 20:13:30 +0100 Subject: [PATCH 153/215] Revert to Arel query and separate filtering --- app/models/spree/user_decorator.rb | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index a8177497b2..9684e86371 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -54,10 +54,22 @@ Spree.user_class.class_eval do self.orders.where(state: :complete).map(&:distributor_id).uniq end + # Returns orders and their associated payments for all distributors that have been ordered from + def get_orders_by_distributor + Enterprise.includes(distributed_orders: { payments: :payment_method }) + .where(enterprises: { id: self.enterprises_ordered_from }, + spree_orders: { state: 'complete', user_id: self.id }) + .order('spree_orders.completed_at DESC') + end + def orders_by_distributor - distributors_with_orders.to_a.sort! do |a, b| - b.distributed_orders.length <=> a.distributed_orders.length + # Remove uncompleted payments as these will not be reflected in order balance + data_array = self.get_orders_by_distributor.to_a.each do |enterprise| + enterprise.distributed_orders.each do |order| + order.payments.keep_if { |payment| payment.state == "completed" } + end end + data_array.sort! { |a, b| b.distributed_orders.length <=> a.distributed_orders.length } end private @@ -67,15 +79,4 @@ Spree.user_class.class_eval do errors.add(:owned_enterprises, "^#{email} is not permitted to own any more enterprises (limit is #{enterprise_limit}).") end end - - def distributors_with_orders - Enterprise - .select("DISTINCT enterprises.*") - .joins("LEFT OUTER JOIN spree_orders ON spree_orders.distributor_id = enterprises.id") - .joins("LEFT OUTER JOIN spree_payments ON spree_payments.order_id = spree_orders.id - AND spree_payments.state = 'completed'") - .joins("LEFT OUTER JOIN spree_payment_methods ON spree_payment_methods.id = spree_payments.payment_method_id") - .where(enterprises: { id: enterprises_ordered_from }, - spree_orders: { state: 'complete', user_id: id }) - end end From d67db76b882d8b086fd959f91b6cd2d407e8a141 Mon Sep 17 00:00:00 2001 From: stveep Date: Mon, 28 Mar 2016 20:35:14 +0100 Subject: [PATCH 154/215] Formatting credit/debit green/red --- app/views/spree/users/_fat.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index f1aa38807e..c10cf8254a 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -16,7 +16,7 @@ %td.order2{"bo-text" => "order.completed_at"} %td.order3.show-for-large-up{"bo-text" => "'spree.payment_states.' + order.payment_state | t | capitalize"} %td.order4.show-for-large-up{"bo-text" => "'spree.shipment_states.' + order.shipment_state | t | capitalize"} - %td.order5.text-right{"bo-text" => "order.total | localizeCurrency"} + %td.order5.text-right{"ng-class" => "{'credit' : order.total < 0, 'debit' : order.total > 0, 'paid' : order.total == 0}","bo-text" => "order.total | localizeCurrency"} %td.order6.text-right.show-for-large-up{"ng-class" => "{'credit' : order.outstanding_balance < 0, 'debit' : order.outstanding_balance > 0, 'paid' : order.outstanding_balance == 0}", "bo-text" => "order.outstanding_balance | localizeCurrency"} %td.order7.text-right{"ng-class" => "{'credit' : order.running_balance < 0, 'debit' : order.running_balance > 0, 'paid' : order.running_balance == 0}", "bo-text" => "order.running_balance | localizeCurrency"} %tr.payment-row{"ng-repeat" => "payment in order.payments"} @@ -24,6 +24,6 @@ %td.order2{"bo-text" => "payment.updated_at"} %td.order3.show-for-large-up{"bo-text" => "payment.payment_method"} %td.order4.show-for-large-up - %td.order5.text-right{"bo-text" => "payment.amount | localizeCurrency"} + %td.order5.text-right{"ng-class" => "{'credit' : payment.amount > 0, 'debit' : payment.amount < 0, 'paid' : payment.amount == 0}","bo-text" => "payment.amount | localizeCurrency"} %td.order6.show-for-large-up %td.order7 From de359403415784d8872b45104b30ac4fd13ec086 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 30 Mar 2016 09:45:45 +1100 Subject: [PATCH 155/215] Apply coding standards --- app/models/spree/user_decorator.rb | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 9684e86371..17473fd521 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -51,24 +51,22 @@ Spree.user_class.class_eval do # Returns Enterprise IDs for distributors that the user has shopped at def enterprises_ordered_from - self.orders.where(state: :complete).map(&:distributor_id).uniq + orders.where(state: :complete).map(&:distributor_id).uniq end # Returns orders and their associated payments for all distributors that have been ordered from - def get_orders_by_distributor - Enterprise.includes(distributed_orders: { payments: :payment_method }) - .where(enterprises: { id: self.enterprises_ordered_from }, - spree_orders: { state: 'complete', user_id: self.id }) - .order('spree_orders.completed_at DESC') + def compelete_orders_by_distributor + Enterprise + .includes(distributed_orders: { payments: :payment_method }) + .where(enterprises: { id: enterprises_ordered_from }, + spree_orders: { state: 'complete', user_id: id }) + .order('spree_orders.completed_at DESC') end def orders_by_distributor # Remove uncompleted payments as these will not be reflected in order balance - data_array = self.get_orders_by_distributor.to_a.each do |enterprise| - enterprise.distributed_orders.each do |order| - order.payments.keep_if { |payment| payment.state == "completed" } - end - end + data_array = compelete_orders_by_distributor.to_a + remove_uncompleted_payments(data_array) data_array.sort! { |a, b| b.distributed_orders.length <=> a.distributed_orders.length } end @@ -79,4 +77,12 @@ Spree.user_class.class_eval do errors.add(:owned_enterprises, "^#{email} is not permitted to own any more enterprises (limit is #{enterprise_limit}).") end end + + def remove_uncompleted_payments(enterprises) + enterprises.each do |enterprise| + enterprise.distributed_orders.each do |order| + order.payments.keep_if { |payment| payment.state == "completed" } + end + end + end end From 3d31a37dd362c8cba03afb989f402f2610d2202c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 30 Mar 2016 12:02:25 +1100 Subject: [PATCH 156/215] Remove unused method Enterprise::find_near --- app/models/enterprise.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 432b53bd4d..84d0ce90b0 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -176,17 +176,6 @@ class Enterprise < ActiveRecord::Base end } - def self.find_near(suburb) - enterprises = [] - - unless suburb.nil? - addresses = Spree::Address.near([suburb.latitude, suburb.longitude], ENTERPRISE_SEARCH_RADIUS, :units => :km).joins(:enterprise).limit(10) - enterprises = addresses.collect(&:enterprise) - end - - enterprises - end - # Force a distinct count to work around relation count issue https://github.com/rails/rails/issues/5554 def self.distinct_count count(distinct: true) From 21be27d72297acca68c64994b00f7d4c035a475d Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 30 Mar 2016 13:11:18 +1100 Subject: [PATCH 157/215] Remove duplicate filtering of visible enterprises Increase readability of enterprises_controller.js.coffee. --- .../darkswarm/controllers/enterprises_controller.js.coffee | 4 ++-- app/views/home/_hubs_table.html.haml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee index db995170dd..ca4a8c45f4 100644 --- a/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee @@ -44,8 +44,8 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, Enterpris $scope.filterEnterprises = -> es = Enterprises.hubs $scope.nameMatches = enterpriseMatchesNameQueryFilter(es, true) - $scope.distanceMatches = enterpriseMatchesNameQueryFilter(es, false) - $scope.distanceMatches = distanceWithinKmFilter($scope.distanceMatches, 50) + noNameMatches = enterpriseMatchesNameQueryFilter(es, false) + $scope.distanceMatches = distanceWithinKmFilter(noNameMatches, 50) $scope.updateVisibleMatches = -> diff --git a/app/views/home/_hubs_table.html.haml b/app/views/home/_hubs_table.html.haml index 8842079f56..13e95ef243 100644 --- a/app/views/home/_hubs_table.html.haml +++ b/app/views/home/_hubs_table.html.haml @@ -1,5 +1,5 @@ .active_table - %hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | visible | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])", + %hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])", "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "ng-controller" => "HubNodeCtrl", id: "{{hub.hash}}"} From db0b3452415e0e18f7869a723123336ee15a03b0 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 31 Mar 2016 15:24:43 +1100 Subject: [PATCH 158/215] Add "Show all on map" button under shops --- app/assets/stylesheets/darkswarm/hubs.css.sass | 5 ++++- app/views/home/_hubs.html.haml | 2 ++ config/locales/en.yml | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/darkswarm/hubs.css.sass b/app/assets/stylesheets/darkswarm/hubs.css.sass index a351170d99..5946d6ee77 100644 --- a/app/assets/stylesheets/darkswarm/hubs.css.sass +++ b/app/assets/stylesheets/darkswarm/hubs.css.sass @@ -7,4 +7,7 @@ @include sidepaddingSm .name-matches, .distance-matches - margin-top: 4em \ No newline at end of file + margin-top: 4em + + .more-controls + text-align: center diff --git a/app/views/home/_hubs.html.haml b/app/views/home/_hubs.html.haml index fea1fc0a7d..3ff19a7a16 100644 --- a/app/views/home/_hubs.html.haml +++ b/app/views/home/_hubs.html.haml @@ -27,3 +27,5 @@ .show-distance-matches{"ng-show" => "nameMatchesFiltered.length > 0 && !distanceMatchesShown"} %a{href: "", "ng-click" => "showDistanceMatches()"} = t :hubs_distance_filter, location: "{{ nameMatchesFiltered[0].name }}" + .more-controls + %a.button{href: main_app.map_path}= t '.show_on_map' diff --git a/config/locales/en.yml b/config/locales/en.yml index 5bf35d1aa5..03c324eb39 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -105,6 +105,10 @@ en: enterprise: select_outgoing_oc_products_from: Select outgoing OC products from + home: + hubs: + show_on_map: "Show all on the map" + # Printable Invoice Columns invoice_column_item: "Item" invoice_column_qty: "Qty" From c4f92fd4cc482ce3b2f88c74de36d99b7fe76f74 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 31 Mar 2016 15:45:25 +1100 Subject: [PATCH 159/215] Hide closed shops by default, add "show" button --- .../controllers/enterprises_controller.js.coffee | 7 +++++++ app/views/home/_hubs.html.haml | 4 ++++ app/views/home/_hubs_table.html.haml | 2 +- config/locales/en.yml | 2 ++ spec/features/consumer/shops_spec.rb | 10 +++++++++- 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee index ca4a8c45f4..fa11c0e92f 100644 --- a/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee @@ -8,6 +8,7 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, Enterpris $scope.show_profiles = false $scope.filtersActive = false $scope.distanceMatchesShown = false + $scope.filterExpression = {active: true} $scope.$watch "query", (query)-> @@ -65,3 +66,9 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, Enterpris $scope.nameMatchesFiltered[0] else undefined + + $scope.showClosedShops = -> + delete $scope.filterExpression['active'] + + $scope.hideClosedShops = -> + $scope.filterExpression['active'] = true diff --git a/app/views/home/_hubs.html.haml b/app/views/home/_hubs.html.haml index 3ff19a7a16..e899270efc 100644 --- a/app/views/home/_hubs.html.haml +++ b/app/views/home/_hubs.html.haml @@ -28,4 +28,8 @@ %a{href: "", "ng-click" => "showDistanceMatches()"} = t :hubs_distance_filter, location: "{{ nameMatchesFiltered[0].name }}" .more-controls + %a.button{href: "", ng: {click: "showClosedShops()", show: "filterExpression.active"}} + = t '.show_closed_shops' + %a.button{href: "", ng: {click: "hideClosedShops()", show: "!filterExpression.active"}} + = t '.hide_closed_shops' %a.button{href: main_app.map_path}= t '.show_on_map' diff --git a/app/views/home/_hubs_table.html.haml b/app/views/home/_hubs_table.html.haml index 13e95ef243..edf9eb5ec8 100644 --- a/app/views/home/_hubs_table.html.haml +++ b/app/views/home/_hubs_table.html.haml @@ -1,5 +1,5 @@ .active_table - %hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])", + %hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | filter:filterExpression | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])", "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "ng-controller" => "HubNodeCtrl", id: "{{hub.hash}}"} diff --git a/config/locales/en.yml b/config/locales/en.yml index 03c324eb39..9525b74e9d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -107,6 +107,8 @@ en: home: hubs: + show_closed_shops: "Show closed shops" + hide_closed_shops: "Hide closed shops" show_on_map: "Show all on the map" # Printable Invoice Columns diff --git a/spec/features/consumer/shops_spec.rb b/spec/features/consumer/shops_spec.rb index faeff9c37d..4edaa702e0 100644 --- a/spec/features/consumer/shops_spec.rb +++ b/spec/features/consumer/shops_spec.rb @@ -26,9 +26,17 @@ feature 'Shops', js: true do page.should_not have_content invisible_distributor.name end - it "should grey out hubs that are not in an order cycle" do + it "should not show hubs that are not in an order cycle" do create(:simple_product, distributors: [d1, d2]) visit shops_path + page.should have_no_selector 'hub.inactive' + page.should have_no_selector 'hub', text: d2.name + end + + it "should show closed shops after clicking the button" do + create(:simple_product, distributors: [d1, d2]) + visit shops_path + click_link "Show closed shops" page.should have_selector 'hub.inactive' page.should have_selector 'hub.inactive', text: d2.name end From 97bcbb81b9a4aa28a6ada414e988c71c1c9a9bc0 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 30 Mar 2016 16:46:06 +1100 Subject: [PATCH 160/215] Change text of register call in the dropdown Unified code for the dropdown and the footer. Both contain the register call and are both pointing to the register path now. The footer still pointed to the global site before. --- app/assets/stylesheets/darkswarm/footer.sass | 2 +- app/views/shared/_footer.html.haml | 8 +------- app/views/shared/_register_call.html.haml | 7 +++++++ app/views/shared/menu/_alert.html.haml | 8 +------- config/locales/en.yml | 6 ++++-- 5 files changed, 14 insertions(+), 17 deletions(-) create mode 100644 app/views/shared/_register_call.html.haml diff --git a/app/assets/stylesheets/darkswarm/footer.sass b/app/assets/stylesheets/darkswarm/footer.sass index 76dd0f4384..a0d2244c0e 100644 --- a/app/assets/stylesheets/darkswarm/footer.sass +++ b/app/assets/stylesheets/darkswarm/footer.sass @@ -30,7 +30,7 @@ footer background-color: transparent border: none padding: 0 - a.big-alert + a.alert-cta @include csstrans width: 100% border: 1px solid rgba($dark-grey, 0.35) diff --git a/app/views/shared/_footer.html.haml b/app/views/shared/_footer.html.haml index fd7f5faf4e..e3e1f44174 100644 --- a/app/views/shared/_footer.html.haml +++ b/app/views/shared/_footer.html.haml @@ -7,13 +7,7 @@ .row .small-12.medium-8.medium-offset-2.columns.text-center .alert-box - %a.big-alert{href: "http://www.openfoodnetwork.org", target: "_blank"} - %h6 - = t :alert_selling_on_ofn -   - %strong - = t :alert_start_here - %i.ofn-i_054-point-right + = render 'shared/register_call' .row .small-12.medium-4.medium-offset-2.columns.text-center %h6 diff --git a/app/views/shared/_register_call.html.haml b/app/views/shared/_register_call.html.haml new file mode 100644 index 0000000000..8c2d95ed2e --- /dev/null +++ b/app/views/shared/_register_call.html.haml @@ -0,0 +1,7 @@ +%a.alert-cta{href: registration_path, target: "_blank"} + %h6 + = t '.selling_on_ofn' +   + %strong + = t '.register' + %i.ofn-i_054-point-right diff --git a/app/views/shared/menu/_alert.html.haml b/app/views/shared/menu/_alert.html.haml index b6ee3acfb1..6512358cc3 100644 --- a/app/views/shared/menu/_alert.html.haml +++ b/app/views/shared/menu/_alert.html.haml @@ -1,10 +1,4 @@ .text-center.page-alert.fixed{ "ofn-page-alert" => true } .alert-box - %a.alert-cta{href: registration_path, target: "_blank"} - %h6 - = t 'alert_selling_on_ofn' -   - %strong - = t 'alert_start_here' - %i.ofn-i_054-point-right + = render 'shared/register_call' %a.close{ ng: { click: "close()" } } × diff --git a/config/locales/en.yml b/config/locales/en.yml index e0ff91d08d..0d4d65e88f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -118,6 +118,10 @@ en: show_closed_shops: "Show closed shops" hide_closed_shops: "Hide closed shops" show_on_map: "Show all on the map" + shared: + register_call: + selling_on_ofn: "Interested in getting on the Open Food Network?" + register: "Register here" shop: messages: login: "login" @@ -167,8 +171,6 @@ en: on_demand: On demand none: None - alert_selling_on_ofn: "Interested in selling food on the Open Food Network?" - alert_start_here: "Start here" label_shops: "Shops" label_map: "Map" label_producers: "Producers" From d12c486dd2d84f9b0f46fb6a3d8a27b4afe6bc65 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 23 Mar 2016 16:23:52 +1100 Subject: [PATCH 161/215] Use new spree_paypal_express branch to hide password --- Gemfile | 7 +++--- Gemfile.lock | 4 ++-- ...7_change_value_type_of_paypal_passwords.rb | 15 ++++++++++++ db/schema.rb | 2 +- spec/features/admin/payment_method_spec.rb | 23 +++++++++++++++++-- 5 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 db/migrate/20160401043927_change_value_type_of_paypal_passwords.rb diff --git a/Gemfile b/Gemfile index 48ff4349cb..d581c8d10b 100644 --- a/Gemfile +++ b/Gemfile @@ -13,9 +13,10 @@ gem 'spree', github: 'openfoodfoundation/spree', branch: '1-3-stable' gem 'spree_i18n', github: 'spree/spree_i18n', branch: '1-3-stable' gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '1-3-stable' -# Waiting on merge of PR #117 -# https://github.com/spree-contrib/better_spree_paypal_express/pull/117 -gem 'spree_paypal_express', :github => "openfoodfoundation/better_spree_paypal_express", :branch => "1-3-stable" +# Our branch contains two changes +# - Pass customer email and phone number to PayPal (merged to upstream master) +# - Change type of password from string to password to hide it in the form +gem 'spree_paypal_express', :github => "openfoodfoundation/better_spree_paypal_express", :branch => "hide-password" #gem 'spree_paypal_express', :github => "spree-contrib/better_spree_paypal_express", :branch => "1-3-stable" gem 'delayed_job_active_record' diff --git a/Gemfile.lock b/Gemfile.lock index fcd0633483..0e31157d96 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,8 +14,8 @@ GIT GIT remote: git://github.com/openfoodfoundation/better_spree_paypal_express.git - revision: cdd61161ccd27cd8d183f9321422c7be113796b8 - branch: 1-3-stable + revision: 840d973cd5bd3250b17674a624dad494aeb09eb3 + branch: hide-password specs: spree_paypal_express (2.0.3) paypal-sdk-merchant (= 1.106.1) diff --git a/db/migrate/20160401043927_change_value_type_of_paypal_passwords.rb b/db/migrate/20160401043927_change_value_type_of_paypal_passwords.rb new file mode 100644 index 0000000000..e03ed25f9e --- /dev/null +++ b/db/migrate/20160401043927_change_value_type_of_paypal_passwords.rb @@ -0,0 +1,15 @@ +class ChangeValueTypeOfPaypalPasswords < ActiveRecord::Migration + def up + Spree::Preference + .where("key like ?", "spree/gateway/pay_pal_express/password/%") + .where(value_type: "string") + .update_all(value_type: "password") + end + + def down + Spree::Preference + .where("key like ?", "spree/gateway/pay_pal_express/password/%") + .where(value_type: "password") + .update_all(value_type: "string") + end +end diff --git a/db/schema.rb b/db/schema.rb index 9994617a5f..abbd788e58 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 => 20160316051131) do +ActiveRecord::Schema.define(:version => 20160401043927) do create_table "account_invoices", :force => true do |t| t.integer "user_id", :null => false diff --git a/spec/features/admin/payment_method_spec.rb b/spec/features/admin/payment_method_spec.rb index caf52c08f8..abb5feaf3e 100644 --- a/spec/features/admin/payment_method_spec.rb +++ b/spec/features/admin/payment_method_spec.rb @@ -30,7 +30,7 @@ feature %q{ payment_method.distributors.should == [@distributors[0]] end - scenario "updating a payment method", retry: 3 do + scenario "updating a payment method" do pm = create(:payment_method, distributors: [@distributors[0]]) login_to_admin_section @@ -42,14 +42,33 @@ feature %q{ check "payment_method_distributor_ids_#{@distributors[1].id}" check "payment_method_distributor_ids_#{@distributors[2].id}" select2_select "PayPal Express", from: "payment_method_type" + expect(page).to have_field 'Login' + fill_in 'payment_method_preferred_login', with: 'testlogin' + fill_in 'payment_method_preferred_password', with: 'secret' + fill_in 'payment_method_preferred_signature', with: 'sig' + click_button 'Update' - flash_message.should eq 'Payment Method has been successfully updated!' + expect(flash_message).to eq 'Payment Method has been successfully updated!' payment_method = Spree::PaymentMethod.find_by_name('New PM Name') expect(payment_method.distributors).to include @distributors[1], @distributors[2] expect(payment_method.distributors).not_to include @distributors[0] expect(payment_method.type).to eq "Spree::Gateway::PayPalExpress" + expect(payment_method.preferences[:login]).to eq 'testlogin' + expect(payment_method.preferences[:password]).to eq 'secret' + expect(payment_method.preferences[:signature]).to eq 'sig' + + fill_in 'payment_method_preferred_login', with: 'otherlogin' + click_button 'Update' + + expect(flash_message).to eq 'Payment Method has been successfully updated!' + expect(page).to have_field 'Password', with: '' + + payment_method = Spree::PaymentMethod.find_by_name('New PM Name') + expect(payment_method.preferences[:login]).to eq 'otherlogin' + expect(payment_method.preferences[:password]).to eq 'secret' + expect(payment_method.preferences[:signature]).to eq 'sig' end end From 3aea387b9a1510a20f9140e8c77740200c368e89 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 4 Mar 2016 15:29:48 +1100 Subject: [PATCH 162/215] First iteration of a model for tag rules --- app/models/enterprise.rb | 8 + app/models/tag_rule.rb | 34 ++++ app/models/tag_rule/discount_order.rb | 25 +++ config/initializers/acts_as_taggable_on.rb | 1 + config/locales/en.yml | 5 + db/migrate/20160303004210_create_tag_rules.rb | 10 + db/schema.rb | 7 + spec/factories.rb | 7 + spec/models/tag_rule/discount_order_spec.rb | 93 +++++++++ spec/models/tag_rule_spec.rb | 184 ++++++++++++++++++ 10 files changed, 374 insertions(+) create mode 100644 app/models/tag_rule.rb create mode 100644 app/models/tag_rule/discount_order.rb create mode 100644 config/initializers/acts_as_taggable_on.rb create mode 100644 db/migrate/20160303004210_create_tag_rules.rb create mode 100644 spec/models/tag_rule/discount_order_spec.rb create mode 100644 spec/models/tag_rule_spec.rb diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 84d0ce90b0..3e1326eff8 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -42,6 +42,7 @@ class Enterprise < ActiveRecord::Base has_many :customers has_many :billable_periods has_many :inventory_items + has_many :tag_rules delegate :latitude, :longitude, :city, :state_name, :to => :address @@ -343,6 +344,13 @@ class Enterprise < ActiveRecord::Base abn.present? end + def apply_tag_rules_to(subject, context) + tag_rules.each do |rule| + rule.set_context(subject,context) + rule.apply + end + end + protected def devise_mailer diff --git a/app/models/tag_rule.rb b/app/models/tag_rule.rb new file mode 100644 index 0000000000..1a3986e2ea --- /dev/null +++ b/app/models/tag_rule.rb @@ -0,0 +1,34 @@ +class TagRule < ActiveRecord::Base + attr_accessor :subject, :context + + belongs_to :enterprise + + preference :customer_tags, :string, default: "" + + validates :enterprise, presence: true + + def set_context(subject, context) + @subject = subject + @context = context + end + + def apply + apply! if relevant? + end + + private + + def relevant? + return false unless subject.class == subject_class + return false unless customer_tags_match? + if respond_to?(:additional_requirements_met?, true) + return false unless additional_requirements_met? + end + true + end + + def customer_tags_match? + context_customer_tags = context.andand[:customer].andand.tag_list || [] + ( context_customer_tags & preferred_customer_tags.split(",") ).any? + end +end diff --git a/app/models/tag_rule/discount_order.rb b/app/models/tag_rule/discount_order.rb new file mode 100644 index 0000000000..1ee9b4263a --- /dev/null +++ b/app/models/tag_rule/discount_order.rb @@ -0,0 +1,25 @@ +class TagRule::DiscountOrder < TagRule + calculated_adjustments + + private + + # Warning: this should only EVER be called via TagRule#apply + def apply! + percentage = "%.2f" % (calculator.preferred_flat_percent * -1) + label = I18n.t("tag_rules.discount_order.label", percentage: percentage) + create_adjustment(label, subject, subject) + end + + def subject_class + Spree::Order + end + + def additional_requirements_met? + return false if already_applied? + true + end + + def already_applied? + subject.adjustments.where(originator_id: id, originator_type: "TagRule").any? + end +end diff --git a/config/initializers/acts_as_taggable_on.rb b/config/initializers/acts_as_taggable_on.rb new file mode 100644 index 0000000000..08d8aa67fc --- /dev/null +++ b/config/initializers/acts_as_taggable_on.rb @@ -0,0 +1 @@ +ActsAsTaggableOn.force_lowercase = true diff --git a/config/locales/en.yml b/config/locales/en.yml index 0d4d65e88f..fe102dff90 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -137,6 +137,11 @@ en: invoice_column_tax: "GST" invoice_column_price: "Price" + # Tag Rules + tag_rules: + discount_order: + label: "%{percentage}% discount" + logo: "Logo (640x130)" #FIXME logo_mobile: "Mobile logo (75x26)" #FIXME logo_mobile_svg: "Mobile logo (SVG)" #FIXME diff --git a/db/migrate/20160303004210_create_tag_rules.rb b/db/migrate/20160303004210_create_tag_rules.rb new file mode 100644 index 0000000000..157fee1e69 --- /dev/null +++ b/db/migrate/20160303004210_create_tag_rules.rb @@ -0,0 +1,10 @@ +class CreateTagRules < ActiveRecord::Migration + def change + create_table :tag_rules do |t| + t.references :enterprise, null: false, index: true + t.string :type, null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index abbd788e58..dc54bd8f3d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1147,6 +1147,13 @@ ActiveRecord::Schema.define(:version => 20160401043927) do t.integer "state_id" end + create_table "tag_rules", :force => true do |t| + t.integer "enterprise_id", :null => false + t.string "type", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "taggings", :force => true do |t| t.integer "tag_id" t.integer "taggable_id" diff --git a/spec/factories.rb b/spec/factories.rb index 8b06b67cad..37611c9d42 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -285,6 +285,13 @@ FactoryGirl.define do year { 2000 + rand(100) } month { 1 + rand(12) } end + + factory :tag_rule, class: TagRule::DiscountOrder do + enterprise { FactoryGirl.create :distributor_enterprise } + before(:create) do |tr| + tr.calculator = Spree::Calculator::FlatPercentItemTotal.new(calculable: tr) + end + end end diff --git a/spec/models/tag_rule/discount_order_spec.rb b/spec/models/tag_rule/discount_order_spec.rb new file mode 100644 index 0000000000..32f2de8874 --- /dev/null +++ b/spec/models/tag_rule/discount_order_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe TagRule::DiscountOrder, type: :model do + let!(:tag_rule) { create(:tag_rule) } + + describe "determining relevance based on additional requirements" do + let(:subject) { double(:subject) } + + before do + tag_rule.set_context(subject,{}) + allow(tag_rule).to receive(:customer_tags_match?) { true } + allow(subject).to receive(:class) { Spree::Order } + end + + context "when already_applied? returns false" do + before { expect(tag_rule).to receive(:already_applied?) { false } } + + it "returns true" do + expect(tag_rule.send(:relevant?)).to be true + end + end + + context "when already_applied? returns true" do + before { expect(tag_rule).to receive(:already_applied?) { true } } + + it "returns false immediately" do + expect(tag_rule.send(:relevant?)).to be false + end + end + end + + describe "determining whether a the rule has already been applied to an order" do + let!(:order) { create(:order) } + let!(:adjustment) { order.adjustments.create({:amount => 12.34, :source => order, :originator => tag_rule, :label => 'discount' }, :without_protection => true) } + + before do + tag_rule.set_context(order, nil) + end + + context "where adjustments originating from the rule already exist" do + it { expect(tag_rule.send(:already_applied?)).to be true} + end + + context "where existing adjustments originate from other rules" do + before { adjustment.update_attribute(:originator_id,create(:tag_rule).id) } + it { expect(tag_rule.send(:already_applied?)).to be false} + end + end + + describe "applying the rule" do + # Assume that all validation is done by the TagRule base class + + let!(:line_item) { create(:line_item, price: 100.00) } + let!(:order) { line_item.order } + + before do + order.update_distribution_charge! + tag_rule.calculator.update_attribute(:preferred_flat_percent, -10.00) + tag_rule.set_context(order, nil) + end + + context "in a simple scenario" do + let(:adjustment) { order.reload.adjustments.where(originator_id: tag_rule, originator_type: "TagRule").first } + + it "creates a new adjustment on the order" do + tag_rule.send(:apply!) + expect(adjustment).to be_a Spree::Adjustment + expect(adjustment.amount).to eq -10.00 + expect(adjustment.label).to eq "10.00% discount" + expect(order.adjustment_total).to eq -10.00 + expect(order.total).to eq 90.00 + end + end + + context "when shipping charges apply" do + let!(:shipping_method) { create(:shipping_method, calculator: Spree::Calculator::FlatRate.new( preferred_amount: 25.00 ) ) } + before do + shipping_method.create_adjustment("Shipping", order, order, true) + end + + let(:adjustment) { order.reload.adjustments.where(originator_id: tag_rule, originator_type: "TagRule").first } + + it "the adjustment is made on line item total, ie. ignores the shipping amount" do + tag_rule.send(:apply!) + expect(adjustment).to be_a Spree::Adjustment + expect(adjustment.amount).to eq -10.00 + expect(adjustment.label).to eq "10.00% discount" + expect(order.adjustment_total).to eq 15.00 + expect(order.total).to eq 115.00 + end + end + end +end diff --git a/spec/models/tag_rule_spec.rb b/spec/models/tag_rule_spec.rb new file mode 100644 index 0000000000..c5107a3dfe --- /dev/null +++ b/spec/models/tag_rule_spec.rb @@ -0,0 +1,184 @@ +require 'spec_helper' + +describe TagRule, type: :model do + let!(:tag_rule) { create(:tag_rule) } + + describe "validations" do + it "requires a enterprise" do + expect(tag_rule).to validate_presence_of :enterprise + end + end + + describe 'setting the context' do + let(:subject) { double(:subject) } + let(:context) { double(:context) } + it "stores the subject and context provided as instance variables on the model" do + tag_rule.set_context(subject, context) + expect(tag_rule.subject).to eq subject + expect(tag_rule.context).to eq context + expect(tag_rule.instance_variable_get(:@subject)).to eq subject + expect(tag_rule.instance_variable_get(:@context)).to eq context + end + end + + describe "determining relevance based on subject and context" do + context "when the subject is nil" do + it "returns false" do + expect(tag_rule.send(:relevant?)).to be false + end + end + + context "when the subject is not nil" do + let(:subject) { double(:subject) } + + before do + tag_rule.set_context(subject,{}) + allow(tag_rule).to receive(:customer_tags_match?) { :customer_tags_match_result } + allow(tag_rule).to receive(:subject_class) { Spree::Order} + end + + + context "when the subject class matches tag_rule#subject_class" do + before do + allow(subject).to receive(:class) { Spree::Order } + end + + context "when customer_tags_match? returns false" do + before { expect(tag_rule).to receive(:customer_tags_match?) { false } } + + it "returns the value of customer_tags_match?" do + expect(tag_rule.send(:relevant?)).to be false + end + end + + context "when customer_tags_match? returns true" do + before { expect(tag_rule).to receive(:customer_tags_match?) { true } } + + context "when the rule does not repond to #additional_requirements_met?" do + before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { false } } + + it "returns true" do + expect(tag_rule.send(:relevant?)).to be true + end + end + + context "when the rule reponds to #additional_requirements_met?" do + before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { true } } + + context "and #additional_requirements_met? returns a truthy value" do + before { allow(tag_rule).to receive(:additional_requirements_met?) { "smeg" } } + + it "returns true immediately" do + expect(tag_rule.send(:relevant?)).to be true + end + end + + context "and #additional_requirements_met? returns true" do + before { allow(tag_rule).to receive(:additional_requirements_met?) { true } } + + it "returns true immediately" do + expect(tag_rule.send(:relevant?)).to be true + end + end + + context "and #additional_requirements_met? returns false" do + before { allow(tag_rule).to receive(:additional_requirements_met?) { false } } + + it "returns false immediately" do + expect(tag_rule.send(:relevant?)).to be false + end + end + end + end + end + + context "when the subject class does not match tag_rule#subject_class" do + before do + allow(subject).to receive(:class) { Spree::LineItem } + end + + it "returns false immediately" do + expect(tag_rule.send(:relevant?)).to be false + expect(tag_rule).to_not have_received :customer_tags_match? + end + end + end + + describe "determining whether specified customer tags match the given context" do + context "when the context is nil" do + before { tag_rule.set_context(nil, nil) } + it "returns false" do + expect(tag_rule.send(:customer_tags_match?)).to be false + end + end + + context "when the context has no customer specified" do + let(:context) { { something_that_is_not_a_customer: double(:something) } } + + before { tag_rule.set_context(nil, context) } + + it "returns false" do + expect(tag_rule.send(:customer_tags_match?)).to be false + end + end + + context "when the context has a customer specified" do + let(:context) { { customer: double(:customer, tag_list: ["member","local","volunteer"] ) } } + + before { tag_rule.set_context(nil, context) } + + context "when the rule has no preferred customer tags specified" do + before do + allow(tag_rule).to receive(:preferred_customer_tags) { "" } + end + + it "returns false" do + expect(tag_rule.send(:customer_tags_match?)).to be false + end + end + + context "when the rule has preferred customer tags specified that match ANY of the customer tags" do + before do + allow(tag_rule).to receive(:preferred_customer_tags) { "wholesale,some_tag,member" } + end + + it "returns false" do + expect(tag_rule.send(:customer_tags_match?)).to be true + end + end + + context "when the rule has preferred customer tags specified that match NONE of the customer tags" do + before do + allow(tag_rule).to receive(:preferred_customer_tags) { "wholesale,some_tag,some_other_tag" } + end + + it "returns false" do + expect(tag_rule.send(:customer_tags_match?)).to be false + end + end + end + end + + describe "applying a tag rule to a subject" do + before { allow(tag_rule).to receive(:apply!) } + + context "when the rule is deemed to be relevant" do + before { allow(tag_rule).to receive(:relevant?) { true } } + + it "applies the rule" do + tag_rule.apply + expect(tag_rule).to have_received(:apply!) + end + end + + context "when the rule is deemed to be relevant" do + before { allow(tag_rule).to receive(:relevant?) { false } } + + it "does not apply the rule" do + tag_rule.apply + expect(tag_rule).to_not have_received(:apply!) + end + end + end + end +end From 066190c16f2e363ed75ee6ab67d3ae2beca1a3d3 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 10 Mar 2016 12:09:27 +1100 Subject: [PATCH 163/215] First cut at an interface for updating tag rules --- .../side_menu_controller.js.coffee | 10 +++---- .../tag_rules_controller.js.coffee | 7 +++++ .../tag_rules/discount_order.js.coffee | 4 +++ .../admin/tag_rules/discount_order.html.haml | 26 +++++++++++++++++++ app/assets/stylesheets/admin/orders.css.scss | 15 +++++++++++ .../stylesheets/admin/tag_rules.css.scss | 13 ++++++++++ .../stylesheets/admin/typography.css.scss | 9 +++++++ .../admin/enterprises_controller.rb | 2 +- app/models/enterprise.rb | 1 + .../flat_percent_item_total_serializer.rb | 7 +++++ .../api/admin/enterprise_serializer.rb | 1 + .../api/admin/tag_rule_serializer.rb | 19 ++++++++++++++ app/views/admin/enterprises/_form.html.haml | 4 +++ .../enterprises/form/_tag_rules.html.haml | 10 +++++++ config/locales/en.yml | 1 + spec/features/admin/enterprises_spec.rb | 25 ++++++++++++++++++ .../tag_rules_controller_spec.js.coffee | 25 ++++++++++++++++++ 17 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee create mode 100644 app/assets/javascripts/admin/enterprises/directives/tag_rules/discount_order.js.coffee create mode 100644 app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml create mode 100644 app/assets/stylesheets/admin/tag_rules.css.scss create mode 100644 app/serializers/api/admin/calculator/flat_percent_item_total_serializer.rb create mode 100644 app/serializers/api/admin/tag_rule_serializer.rb create mode 100644 app/views/admin/enterprises/form/_tag_rules.html.haml create mode 100644 spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee diff --git a/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee index 7981e498b6..a0105fefa4 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee @@ -17,8 +17,9 @@ angular.module("admin.enterprises") { name: t('shipping_methods'), icon_class: "icon-truck", show: "showShippingMethods()" } { name: t('payment_methods'), icon_class: "icon-money", show: "showPaymentMethods()" } { name: t('enterprise_fees'), icon_class: "icon-tasks", show: "showEnterpriseFees()" } - { name: t('inventory_settings'), icon_class: "icon-list-ol", show: "showInventorySettings()" } - { name: t('shop_preferences'), icon_class: "icon-shopping-cart", show: "showShopPreferences()" } + { name: t('inventory_settings'), icon_class: "icon-list-ol", show: "enterpriseIsShop()" } + { name: t('tag_rules'), icon_class: "icon-random", show: "enterpriseIsShop()" } + { name: t('shop_preferences'), icon_class: "icon-shopping-cart", show: "enterpriseIsShop()" } ] $scope.select(0) @@ -42,8 +43,5 @@ angular.module("admin.enterprises") $scope.showEnterpriseFees = -> enterprisePermissions.can_manage_enterprise_fees && ($scope.Enterprise.sells != "none" || $scope.Enterprise.is_primary_producer) - $scope.showInventorySettings = -> - $scope.Enterprise.sells != "none" - - $scope.showShopPreferences = -> + $scope.enterpriseIsShop = -> $scope.Enterprise.sells != "none" diff --git a/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee new file mode 100644 index 0000000000..4b007c7744 --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee @@ -0,0 +1,7 @@ +angular.module("admin.enterprises").controller "TagRulesCtrl", ($scope) -> + $scope.groupedTagRules = $scope.Enterprise.tag_rules.reduce (groupedTagRules, rule) -> + key = rule.preferred_customer_tags + groupedTagRules[key] ||= [] + groupedTagRules[key].push rule + groupedTagRules + , {} diff --git a/app/assets/javascripts/admin/enterprises/directives/tag_rules/discount_order.js.coffee b/app/assets/javascripts/admin/enterprises/directives/tag_rules/discount_order.js.coffee new file mode 100644 index 0000000000..079dd04dba --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/directives/tag_rules/discount_order.js.coffee @@ -0,0 +1,4 @@ +angular.module("admin.enterprises").directive "discountOrder", -> + restrict: "E" + replace: true + templateUrl: "admin/tag_rules/discount_order.html" diff --git a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml new file mode 100644 index 0000000000..19b4af427e --- /dev/null +++ b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml @@ -0,0 +1,26 @@ +%div + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{rule.id}}_id", + name: "enterprise[tag_rules_attributes][{{rule.id}}][id]", + ng: { value: "rule.id" } } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{rule.id}}_calculator_attributes_id", + name: "enterprise[tag_rules_attributes][{{rule.id}}][calculator_attributes][id]", + ng: { value: "rule.calculator.id" } } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{rule.id}}_calculator_attributes_type", + name: "enterprise[tag_rules_attributes][{{rule.id}}][calculator_attributes][type]", + value: "TagRule::FlatPercentItemTotal" } + + %span.text-big Apply a discount of + %span.input-symbol.after + %span.text-big % + %input.text-big{ type: "number", + id: "enterprise_tag_rules_attributes_{{rule.id}}_calculator_attributes_preferred_flat_percent", + name: "enterprise[tag_rules_attributes][{{rule.id}}][calculator_attributes][preferred_flat_percent]", + min: 0, + max: 100, + ng: { model: "rule.calculator.preferred_flat_percent" } } + %span.text-big to order subtotals diff --git a/app/assets/stylesheets/admin/orders.css.scss b/app/assets/stylesheets/admin/orders.css.scss index 9a2dd4385c..761ccbc014 100644 --- a/app/assets/stylesheets/admin/orders.css.scss +++ b/app/assets/stylesheets/admin/orders.css.scss @@ -69,6 +69,21 @@ div#group_buy_calculation { text-indent:1em; } } + + &.after { + + span { + position: absolute; + transform: translate(0,-55%); + top:50%; + right: 0.5em; + pointer-events:none; + } + + input { + padding-right: 1.2em; + } + } } th.actions { diff --git a/app/assets/stylesheets/admin/tag_rules.css.scss b/app/assets/stylesheets/admin/tag_rules.css.scss new file mode 100644 index 0000000000..eafc5826f7 --- /dev/null +++ b/app/assets/stylesheets/admin/tag_rules.css.scss @@ -0,0 +1,13 @@ +.customer_tag { + border: 1px solid #cee1f4; + + .header { + padding: 8px 10px; + background-color: #eff5fc; + border-bottom: 1px solid #cee1f4; + } + + .tag_rule { + padding: 8px 10px; + } +} diff --git a/app/assets/stylesheets/admin/typography.css.scss b/app/assets/stylesheets/admin/typography.css.scss index 20148df3f1..761058fb1d 100644 --- a/app/assets/stylesheets/admin/typography.css.scss +++ b/app/assets/stylesheets/admin/typography.css.scss @@ -7,3 +7,12 @@ font-size: 1.2rem; font-weight: 300; } + +.text-red { + color: #DA5354; +} + + +input.text-big { + font-size: 1.1rem; +} diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 55eaabd7d8..d692984885 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -4,7 +4,7 @@ module Admin class EnterprisesController < ResourceController before_filter :load_enterprise_set, :only => :index before_filter :load_countries, :except => [:index, :register, :check_permalink] - before_filter :load_methods_and_fees, :only => [:new, :edit, :update, :create] + before_filter :load_methods_and_fees, :only => [:edit, :update] 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 diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 3e1326eff8..62462ac425 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -48,6 +48,7 @@ class Enterprise < ActiveRecord::Base accepts_nested_attributes_for :address accepts_nested_attributes_for :producer_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? } + accepts_nested_attributes_for :tag_rules, allow_destroy: true has_attached_file :logo, styles: { medium: "300x300>", small: "180x180>", thumb: "100x100>" }, diff --git a/app/serializers/api/admin/calculator/flat_percent_item_total_serializer.rb b/app/serializers/api/admin/calculator/flat_percent_item_total_serializer.rb new file mode 100644 index 0000000000..7662edb3f6 --- /dev/null +++ b/app/serializers/api/admin/calculator/flat_percent_item_total_serializer.rb @@ -0,0 +1,7 @@ +class Api::Admin::Calculator::FlatPercentItemTotalSerializer < ActiveModel::Serializer + attributes :id, :preferred_flat_percent + + def preferred_flat_percent + object.preferred_flat_percent.to_i + end +end diff --git a/app/serializers/api/admin/enterprise_serializer.rb b/app/serializers/api/admin/enterprise_serializer.rb index 37a96b402f..1d9c37595d 100644 --- a/app/serializers/api/admin/enterprise_serializer.rb +++ b/app/serializers/api/admin/enterprise_serializer.rb @@ -7,4 +7,5 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer has_one :owner, serializer: Api::Admin::UserSerializer has_many :users, serializer: Api::Admin::UserSerializer + has_many :tag_rules, serializer: Api::Admin::TagRuleSerializer end diff --git a/app/serializers/api/admin/tag_rule_serializer.rb b/app/serializers/api/admin/tag_rule_serializer.rb new file mode 100644 index 0000000000..a571b9fe5e --- /dev/null +++ b/app/serializers/api/admin/tag_rule_serializer.rb @@ -0,0 +1,19 @@ +class Api::Admin::TagRuleSerializer < ActiveModel::Serializer + def serializable_hash + rule_specific_serializer.serializable_hash + end + + def rule_specific_serializer + "Api::Admin::#{object.class.to_s}Serializer".constantize.new(object) + end +end + +module Api::Admin::TagRule + class BaseSerializer < ActiveModel::Serializer + attributes :id, :enterprise_id, :type, :preferred_customer_tags + end + + class DiscountOrderSerializer < BaseSerializer + has_one :calculator, serializer: Api::Admin::Calculator::FlatPercentItemTotalSerializer + end +end diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index 79faeea229..d602f7418d 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -54,3 +54,7 @@ %fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Shop Preferences'" } } %legend Shop Preferences = render 'admin/enterprises/form/shop_preferences', f: f + +%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Tag Rules'" } } + %legend Tag Rules + = render 'admin/enterprises/form/tag_rules', f: f diff --git a/app/views/admin/enterprises/form/_tag_rules.html.haml b/app/views/admin/enterprises/form/_tag_rules.html.haml new file mode 100644 index 0000000000..44b1148058 --- /dev/null +++ b/app/views/admin/enterprises/form/_tag_rules.html.haml @@ -0,0 +1,10 @@ +.row{ ng: { controller: "TagRulesCtrl" } } + .eleven.columns.alpha.omega + .eleven.columns.alpha.omega + .customer_tag{ ng: { repeat: "(tags, rules) in groupedTagRules" }, bindonce: true } + .header + %h3 + For customers tagged + %span.text-red #{{ tags.split(",").join(", #") }} + .tag_rule{ ng: { repeat: "rule in rules" } } + %discount-order{ bo: { if: "rule.type == 'TagRule::DiscountOrder'" } } diff --git a/config/locales/en.yml b/config/locales/en.yml index fe102dff90..7892ecdce1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1001,6 +1001,7 @@ Please follow the instructions there to make your enterprise visible on the Open payment_methods: "Payment Methods" enterprise_fees: "Enterprise Fees" inventory_settings: "Inventory Settings" + tag_rules: "Tag Rules" shop_preferences: "Shop Preferences" validation_msg_relationship_already_established: "^That relationship is already established." validation_msg_at_least_one_hub: "^At least one hub must be selected" diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index b72258f7f9..3e9008968b 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -282,6 +282,31 @@ feature %q{ end end + describe "tag rules", js: true do + let!(:enterprise) { create(:distributor_enterprise) } + + context "updating" do + let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } + + before do + login_to_admin_section + visit main_app.edit_admin_enterprise_path(enterprise) + end + + it "saves changes to the rule" do + click_link "Tag Rules" + + expect(first('.customer_tag .header')).to have_content "For customers tagged #member" + expect(page).to have_input "enterprise[tag_rules_attributes][#{tag_rule.id}][calculator_attributes][preferred_flat_percent]", with: "0" + fill_in "enterprise[tag_rules_attributes][#{tag_rule.id}][calculator_attributes][preferred_flat_percent]", with: 45 + + click_button 'Update' + + expect(tag_rule.calculator.preferred_flat_percent).to eq 45 + end + end + end + context "as an Enterprise user", js: true do let(:supplier1) { create(:supplier_enterprise, name: 'First Supplier') } diff --git a/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee new file mode 100644 index 0000000000..1b4ca0f34e --- /dev/null +++ b/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee @@ -0,0 +1,25 @@ +describe "TagRulesCtrl", -> + ctrl = null + scope = null + enterprise = null + + beforeEach -> + module('admin.enterprises') + enterprise = + tag_rules: [ + { id: 1, preferred_customer_tags: "member" }, + { id: 2, preferred_customer_tags: "member" }, + { id: 3, preferred_customer_tags: "local" } + ] + + inject ($rootScope, $controller) -> + scope = $rootScope + scope.Enterprise = enterprise + ctrl = $controller 'TagRulesCtrl', {$scope: scope} + + describe "initialization", -> + it "groups rules by preferred_customer_tags", -> + expect(scope.groupedTagRules).toEqual { + member: [{ id: 1, preferred_customer_tags: "member" }, { id: 2, preferred_customer_tags: "member" }], + local: [{ id: 3, preferred_customer_tags: "local" }] + } From 4c2552e0bfb6293dda5a4055d70f2137ca6dc874 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 11 Mar 2016 15:56:54 +1100 Subject: [PATCH 164/215] Enterprise users can create tag rules --- .../tag_rules_controller.js.coffee | 31 ++++++++--- .../admin/enterprises/enterprises.js.coffee | 2 +- .../admin/tag_rules/discount_order.html.haml | 36 ++++++++----- app/assets/stylesheets/admin/offsets.css.scss | 4 ++ .../stylesheets/admin/tag_rules.css.scss | 22 ++++++++ .../admin/enterprises_controller.rb | 23 ++++++++ app/models/enterprise.rb | 2 +- app/models/tag_rule.rb | 2 + .../api/admin/enterprise_serializer.rb | 20 ++++++- app/views/admin/enterprises/_form.html.haml | 2 +- app/views/admin/enterprises/edit.html.haml | 1 + .../enterprises/form/_tag_rules.html.haml | 19 +++++-- .../admin/enterprises_controller_spec.rb | 52 +++++++++++++++++++ spec/features/admin/enterprises_spec.rb | 35 +++++++++++-- .../tag_rules_controller_spec.js.coffee | 22 ++++---- 15 files changed, 232 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee index 4b007c7744..e8c95558df 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee @@ -1,7 +1,26 @@ angular.module("admin.enterprises").controller "TagRulesCtrl", ($scope) -> - $scope.groupedTagRules = $scope.Enterprise.tag_rules.reduce (groupedTagRules, rule) -> - key = rule.preferred_customer_tags - groupedTagRules[key] ||= [] - groupedTagRules[key].push rule - groupedTagRules - , {} + $scope.tagGroups = $scope.Enterprise.tag_groups + + updateRuleCounts = -> + index = 0 + for tagGroup in $scope.tagGroups + tagGroup.startIndex = index + index = index + tagGroup.rules.length + + updateRuleCounts() + + $scope.updateTagsRulesFor = (tagGroup) -> + for tagRule in tagGroup.rules + tagRule.preferred_customer_tags = (tag.text for tag in tagGroup.tags).join(",") + + $scope.addNewRuleTo = (tagGroup) -> + tagGroup.rules.push + id: null + preferred_customer_tags: (tag.text for tag in tagGroup.tags).join(",") + type: "TagRule::DiscountOrder" + calculator: + preferred_flat_percent: 0 + updateRuleCounts() + + $scope.addNewTag = -> + $scope.tagGroups.push { tags: [], rules: [] } diff --git a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee index 6be7e00ffa..4537620512 100644 --- a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee +++ b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee @@ -1 +1 @@ -angular.module("admin.enterprises", [ "admin.payment_methods", "admin.utils", "admin.shipping_methods", "admin.users", "textAngular", "admin.side_menu", "admin.taxons", 'admin.indexUtils', 'admin.dropdown', 'pasvaz.bindonce', 'ngSanitize'] ) \ No newline at end of file +angular.module("admin.enterprises", [ "admin.payment_methods", "admin.utils", "admin.shipping_methods", "admin.users", "textAngular", "admin.side_menu", "admin.taxons", 'admin.indexUtils', 'admin.dropdown', 'pasvaz.bindonce', 'ngSanitize', 'ngTagsInput'] ) \ No newline at end of file diff --git a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml index 19b4af427e..bc2ddc5c75 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml @@ -1,26 +1,36 @@ %div %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{rule.id}}_id", - name: "enterprise[tag_rules_attributes][{{rule.id}}][id]", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_id", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][id]", ng: { value: "rule.id" } } %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{rule.id}}_calculator_attributes_id", - name: "enterprise[tag_rules_attributes][{{rule.id}}][calculator_attributes][id]", - ng: { value: "rule.calculator.id" } } + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_type", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][type]", + value: "TagRule::DiscountOrder" } %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{rule.id}}_calculator_attributes_type", - name: "enterprise[tag_rules_attributes][{{rule.id}}][calculator_attributes][type]", - value: "TagRule::FlatPercentItemTotal" } + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_customer_tags", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_customer_tags]", + ng: { value: "rule.preferred_customer_tags" } } - %span.text-big Apply a discount of + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_type", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][calculator_type]", + value: "Spree::Calculator::FlatPercentItemTotal" } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_id", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][calculator_attributes][id]", + ng: { value: "rule.calculator.id" } } + + %span.text-normal {{ $index + 1 }}. Apply a discount of %span.input-symbol.after - %span.text-big % + %span.text-normal % %input.text-big{ type: "number", - id: "enterprise_tag_rules_attributes_{{rule.id}}_calculator_attributes_preferred_flat_percent", - name: "enterprise[tag_rules_attributes][{{rule.id}}][calculator_attributes][preferred_flat_percent]", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][calculator_attributes][preferred_flat_percent]", min: 0, max: 100, ng: { model: "rule.calculator.preferred_flat_percent" } } - %span.text-big to order subtotals + %span.text-normal to order subtotals diff --git a/app/assets/stylesheets/admin/offsets.css.scss b/app/assets/stylesheets/admin/offsets.css.scss index 762b7469f6..190ed49243 100644 --- a/app/assets/stylesheets/admin/offsets.css.scss +++ b/app/assets/stylesheets/admin/offsets.css.scss @@ -2,6 +2,10 @@ margin-bottom: 20px; } +.margin-bottom-30 { + margin-bottom: 30px; +} + .margin-bottom-50 { margin-bottom: 50px; } diff --git a/app/assets/stylesheets/admin/tag_rules.css.scss b/app/assets/stylesheets/admin/tag_rules.css.scss index eafc5826f7..cbaed55587 100644 --- a/app/assets/stylesheets/admin/tag_rules.css.scss +++ b/app/assets/stylesheets/admin/tag_rules.css.scss @@ -1,5 +1,13 @@ +.no_tags { + margin-bottom: 40px; + color: #aeaeae; + font-size: 1rem; + font-weight: bold; +} + .customer_tag { border: 1px solid #cee1f4; + margin-bottom: 40px; .header { padding: 8px 10px; @@ -7,7 +15,21 @@ border-bottom: 1px solid #cee1f4; } + .no_rules { + padding: 8px 10px; + margin-bottom: 10px; + color: #aeaeae; + font-size: 1rem; + font-weight: bold; + } + .tag_rule { padding: 8px 10px; + margin-bottom: 10px; + } + + .add_rule { + padding: 8px 10px; + margin-bottom: 10px; } } diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index d692984885..377186b604 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -35,6 +35,8 @@ module Admin def update invoke_callbacks(:update, :before) + tag_rules_attributes = params[object_name].delete :tag_rules_attributes + update_tag_rules(tag_rules_attributes) if tag_rules_attributes.present? if @object.update_attributes(params[object_name]) invoke_callbacks(:update, :after) flash[:success] = flash_message_for(@object, :successfully_updated) @@ -180,6 +182,27 @@ module Admin @taxons = Spree::Taxon.order(:name) end + def update_tag_rules(tag_rules_attributes) + # Due to the combination of trying to use nested attributes and type inheritance + # we cannot apply all attributes to tag rules in one hit because mass assignment + # methods that are specific to each class do not become available until after the + # record is persisted. This problem is compounded by the use of calculators. + @object.transaction do + tag_rules_attributes.select{ |i, attrs| attrs[:type].present? }.each do |i, attrs| + rule = @object.tag_rules.find_by_id(attrs.delete :id) || attrs[:type].constantize.new(enterprise: @object) + create_calculator_for(rule, attrs) if rule.type == "TagRule::DiscountOrder" && rule.calculator.nil? + rule.update_attributes(attrs) + end + end + end + + def create_calculator_for(rule, attrs) + if attrs[:calculator_type].present? && attrs[:calculator_attributes].present? + rule.update_attributes(calculator_type: attrs[:calculator_type]) + attrs[:calculator_attributes].merge!( { id: rule.calculator.id } ) + end + end + def check_can_change_bulk_sells unless spree_current_user.admin? params[:enterprise_set][:collection_attributes].each do |i, enterprise_params| diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 62462ac425..dfc5050a38 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -48,7 +48,7 @@ class Enterprise < ActiveRecord::Base accepts_nested_attributes_for :address accepts_nested_attributes_for :producer_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? } - accepts_nested_attributes_for :tag_rules, allow_destroy: true + accepts_nested_attributes_for :tag_rules, allow_destroy: true, reject_if: lambda { |tag_rule| tag_rule[:preferred_customer_tags].blank? } has_attached_file :logo, styles: { medium: "300x300>", small: "180x180>", thumb: "100x100>" }, diff --git a/app/models/tag_rule.rb b/app/models/tag_rule.rb index 1a3986e2ea..cf0a105e80 100644 --- a/app/models/tag_rule.rb +++ b/app/models/tag_rule.rb @@ -7,6 +7,8 @@ class TagRule < ActiveRecord::Base validates :enterprise, presence: true + attr_accessible :enterprise, :enterprise_id, :preferred_customer_tags + def set_context(subject, context) @subject = subject @context = context diff --git a/app/serializers/api/admin/enterprise_serializer.rb b/app/serializers/api/admin/enterprise_serializer.rb index 1d9c37595d..8f70da1a0d 100644 --- a/app/serializers/api/admin/enterprise_serializer.rb +++ b/app/serializers/api/admin/enterprise_serializer.rb @@ -3,9 +3,25 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer attributes :producer_profile_only, :email, :long_description, :permalink attributes :preferred_shopfront_message, :preferred_shopfront_closed_message, :preferred_shopfront_taxon_order, :preferred_shopfront_order_cycle_order attributes :preferred_product_selection_from_inventory_only - attributes :owner, :users + attributes :owner, :users, :tag_groups has_one :owner, serializer: Api::Admin::UserSerializer has_many :users, serializer: Api::Admin::UserSerializer - has_many :tag_rules, serializer: Api::Admin::TagRuleSerializer + + def tag_groups + tag_groups = [] + object.tag_rules.each do |tag_rule| + tag_group = find_match(tag_groups, tag_rule.preferred_customer_tags.split(",").map{ |t| { text: t } }) + tag_groups << tag_group if tag_group[:rules].empty? + tag_group[:rules] << Api::Admin::TagRuleSerializer.new(tag_rule).serializable_hash + end + tag_groups + end + + def find_match(tag_groups, tags) + tag_groups.each do |tag_group| + return tag_group if tag_group[:tags].length == tags.length && (tag_group[:tags] & tags) == tag_group[:tags] + end + return { tags: tags, rules: [] } + end end diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index d602f7418d..e363078733 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -55,6 +55,6 @@ %legend Shop Preferences = render 'admin/enterprises/form/shop_preferences', f: f -%fieldset.alpha.no-border-bottom{ ng: { show: "menu.selected.name=='Tag Rules'" } } +%fieldset.alpha.no-border-bottom{ ng: { if: "menu.selected.name=='Tag Rules'" } } %legend Tag Rules = render 'admin/enterprises/form/tag_rules', f: f diff --git a/app/views/admin/enterprises/edit.html.haml b/app/views/admin/enterprises/edit.html.haml index 19ae0f3b44..5d3a623f40 100644 --- a/app/views/admin/enterprises/edit.html.haml +++ b/app/views/admin/enterprises/edit.html.haml @@ -7,6 +7,7 @@ - content_for :page_actions do %li= button_link_to "Back to enterprises list", main_app.admin_enterprises_path, icon: 'icon-arrow-left' + = render 'admin/enterprises/form_data' = render 'admin/enterprises/ng_form', action: 'edit' diff --git a/app/views/admin/enterprises/form/_tag_rules.html.haml b/app/views/admin/enterprises/form/_tag_rules.html.haml index 44b1148058..ecb8b54f2e 100644 --- a/app/views/admin/enterprises/form/_tag_rules.html.haml +++ b/app/views/admin/enterprises/form/_tag_rules.html.haml @@ -1,10 +1,21 @@ .row{ ng: { controller: "TagRulesCtrl" } } .eleven.columns.alpha.omega .eleven.columns.alpha.omega - .customer_tag{ ng: { repeat: "(tags, rules) in groupedTagRules" }, bindonce: true } + .no_tags{ ng: { show: "tagGroups.length == 0" } } + No tags apply to this enterprise yet + .customer_tag{ ng: { repeat: "tagGroup in tagGroups" }, bindonce: true } .header %h3 - For customers tagged - %span.text-red #{{ tags.split(",").join(", #") }} - .tag_rule{ ng: { repeat: "rule in rules" } } + For customers tagged: + %tags-input{ ng: { model: 'tagGroup.tags'}, + min: { tags: "1" }, + on: { tag: { added: "updateTagsRulesFor(tagGroup)", removed: "updateTagsRulesFor(tagGroup)" } } } + + .no_rules{ ng: { show: "tagGroup.rules.length == 0" } } + No rules apply to this tag yet + .tag_rule{ ng: { repeat: "rule in tagGroup.rules" } } %discount-order{ bo: { if: "rule.type == 'TagRule::DiscountOrder'" } } + .add_rule + %input.button.icon-plus{ type: 'button', value: "+ Add A New Rule", ng: { click: 'addNewRuleTo(tagGroup)' } } + .add_tag + %input.button.icon-plus{ type: 'button', value: "+ Add A New Tag", ng: { click: 'addNewTag()' } } diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 8b972a3321..05498010bb 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -181,6 +181,58 @@ module Admin end end end + + describe "tag rules" do + let(:enterprise) { create(:distributor_enterprise) } + let!(:tag_rule) { create(:tag_rule, enterprise: enterprise) } + + before do + login_as_enterprise_user [enterprise] + end + + context "discount order rules" do + it "updates the existing rule with new attributes" do + spree_put :update, { + id: enterprise, + enterprise: { + tag_rules_attributes: { + '0' => { + id: tag_rule, + type: "TagRule::DiscountOrder", + preferred_customer_tags: "some,new,tags", + calculator_type: "Spree::Calculator::FlatPercentItemTotal", + calculator_attributes: { id: tag_rule.calculator.id, preferred_flat_percent: "15" } + } + } + } + } + tag_rule.reload + expect(tag_rule.preferred_customer_tags).to eq "some,new,tags" + expect(tag_rule.calculator.preferred_flat_percent).to eq 15 + end + + it "creates new rules with new attributes" do + spree_put :update, { + id: enterprise, + enterprise: { + tag_rules_attributes: { + '0' => { + id: "", + type: "TagRule::DiscountOrder", + preferred_customer_tags: "tags,are,awesome", + calculator_type: "Spree::Calculator::FlatPercentItemTotal", + calculator_attributes: { id: "", preferred_flat_percent: "24" } + } + } + } + } + expect(tag_rule.reload).to be + new_tag_rule = TagRule::DiscountOrder.last + expect(new_tag_rule.preferred_customer_tags).to eq "tags,are,awesome" + expect(new_tag_rule.calculator.preferred_flat_percent).to eq 24 + end + end + end end context "as owner" do diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 3e9008968b..2b77d6e18b 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -285,6 +285,32 @@ feature %q{ describe "tag rules", js: true do let!(:enterprise) { create(:distributor_enterprise) } + context "creating" do + before do + login_to_admin_section + visit main_app.edit_admin_enterprise_path(enterprise) + end + + it "creates a new rule" do + click_link "Tag Rules" + + expect(page).to_not have_selector '.customer_tag' + expect(page).to have_content 'No tags apply to this enterprise yet' + click_button '+ Add A New Tag' + find(:css, "tags-input .tags input").set "volunteer\n" + + expect(page).to have_content 'No rules apply to this tag yet' + click_button '+ Add A New Rule' + fill_in "enterprise[tag_rules_attributes][0][calculator_attributes][preferred_flat_percent]", with: 22 + + click_button 'Update' + + tag_rule = TagRule::DiscountOrder.last + expect(tag_rule.preferred_customer_tags).to eq "volunteer" + expect(tag_rule.calculator.preferred_flat_percent).to eq 22 + end + end + context "updating" do let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } @@ -296,12 +322,15 @@ feature %q{ it "saves changes to the rule" do click_link "Tag Rules" - expect(first('.customer_tag .header')).to have_content "For customers tagged #member" - expect(page).to have_input "enterprise[tag_rules_attributes][#{tag_rule.id}][calculator_attributes][preferred_flat_percent]", with: "0" - fill_in "enterprise[tag_rules_attributes][#{tag_rule.id}][calculator_attributes][preferred_flat_percent]", with: 45 + expect(first('.customer_tag .header')).to have_content "For customers tagged:" + expect(first('tags-input .tag-list ti-tag-item')).to have_content "member" + find(:css, "tags-input .tags input").set "volunteer\n" + expect(page).to have_input "enterprise[tag_rules_attributes][0][calculator_attributes][preferred_flat_percent]", with: "0" + fill_in "enterprise[tag_rules_attributes][0][calculator_attributes][preferred_flat_percent]", with: 45 click_button 'Update' + expect(tag_rule.preferred_customer_tags).to eq "member,volunteer" expect(tag_rule.calculator.preferred_flat_percent).to eq 45 end end diff --git a/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee index 1b4ca0f34e..dd2fcee449 100644 --- a/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee @@ -6,10 +6,9 @@ describe "TagRulesCtrl", -> beforeEach -> module('admin.enterprises') enterprise = - tag_rules: [ - { id: 1, preferred_customer_tags: "member" }, - { id: 2, preferred_customer_tags: "member" }, - { id: 3, preferred_customer_tags: "local" } + tag_groups: [ + { tags: "member", rules: [{ id: 1, preferred_customer_tags: "member" }, { id: 2, preferred_customer_tags: "member" }] }, + { tags: "volunteer", rules: [{ id: 3, preferred_customer_tags: "local" }] } ] inject ($rootScope, $controller) -> @@ -17,9 +16,12 @@ describe "TagRulesCtrl", -> scope.Enterprise = enterprise ctrl = $controller 'TagRulesCtrl', {$scope: scope} - describe "initialization", -> - it "groups rules by preferred_customer_tags", -> - expect(scope.groupedTagRules).toEqual { - member: [{ id: 1, preferred_customer_tags: "member" }, { id: 2, preferred_customer_tags: "member" }], - local: [{ id: 3, preferred_customer_tags: "local" }] - } + describe "tagGroup start indices", -> + it "updates on initialization", -> + expect(scope.tagGroups[0].startIndex).toEqual 0 + expect(scope.tagGroups[1].startIndex).toEqual 2 + + it "updates when tags are added to a tagGroup", -> + scope.addNewRuleTo(scope.tagGroups[0]) + expect(scope.tagGroups[0].startIndex).toEqual 0 + expect(scope.tagGroups[1].startIndex).toEqual 3 From bf72864c2bf095782509370d625b89f1e8788335 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 16 Mar 2016 11:29:35 +1100 Subject: [PATCH 165/215] Tag Rules can be deleted --- .../tag_rules_controller.js.coffee | 17 +++++- .../stylesheets/admin/tag_rules.css.scss | 32 +++++++++-- app/controllers/admin/tag_rules_controller.rb | 10 ++++ app/models/spree/ability_decorator.rb | 4 ++ .../enterprises/form/_tag_rules.html.haml | 31 +++++++---- config/routes.rb | 2 + .../admin/tag_rules_controller_spec.rb | 36 +++++++++++++ spec/features/admin/enterprises_spec.rb | 22 ++++++++ .../tag_rules_controller_spec.js.coffee | 54 ++++++++++++++++++- 9 files changed, 193 insertions(+), 15 deletions(-) create mode 100644 app/controllers/admin/tag_rules_controller.rb create mode 100644 spec/controllers/admin/tag_rules_controller_spec.rb diff --git a/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee index e8c95558df..096cce250d 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.enterprises").controller "TagRulesCtrl", ($scope) -> +angular.module("admin.enterprises").controller "TagRulesCtrl", ($scope, $http) -> $scope.tagGroups = $scope.Enterprise.tag_groups updateRuleCounts = -> @@ -24,3 +24,18 @@ angular.module("admin.enterprises").controller "TagRulesCtrl", ($scope) -> $scope.addNewTag = -> $scope.tagGroups.push { tags: [], rules: [] } + + $scope.deleteTagRule = (tagGroup, tagRule) -> + index = tagGroup.rules.indexOf(tagRule) + return unless index >= 0 + if tagRule.id is null + tagGroup.rules.splice(index, 1) + updateRuleCounts() + else + if confirm("Are you sure?") + $http + method: "DELETE" + url: "/admin/enterprises/#{$scope.Enterprise.id}/tag_rules/#{tagRule.id}.json" + .success -> + tagGroup.rules.splice(index, 1) + updateRuleCounts() diff --git a/app/assets/stylesheets/admin/tag_rules.css.scss b/app/assets/stylesheets/admin/tag_rules.css.scss index cbaed55587..985ed7d727 100644 --- a/app/assets/stylesheets/admin/tag_rules.css.scss +++ b/app/assets/stylesheets/admin/tag_rules.css.scss @@ -13,6 +13,16 @@ padding: 8px 10px; background-color: #eff5fc; border-bottom: 1px solid #cee1f4; + + table { + padding: 0px; + margin: 0px 0px 0px 0px; + tr { + td { + border: none; + } + } + } } .no_rules { @@ -23,9 +33,25 @@ font-weight: bold; } - .tag_rule { - padding: 8px 10px; - margin-bottom: 10px; + table { + padding: 0px; + margin: 0px 0px 10px 0px; + + tr.tag_rule { + border: none; + padding: 0px; + margin: 0px; + + td { + border: none; + padding: 4px 10px; + margin: 0px; + + input { + width: auto; + } + } + } } .add_rule { diff --git a/app/controllers/admin/tag_rules_controller.rb b/app/controllers/admin/tag_rules_controller.rb new file mode 100644 index 0000000000..7d60cb4888 --- /dev/null +++ b/app/controllers/admin/tag_rules_controller.rb @@ -0,0 +1,10 @@ +module Admin + class TagRulesController < ResourceController + + respond_to :json + + respond_override destroy: { json: { + success: lambda { render nothing: true, :status => 204 } + } } + end +end diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 8c93fe4b71..c234cf1358 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -72,6 +72,10 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], ProducerProperty + can [:admin, :destroy], TagRule do |tag_rule| + user.enterprises.include? tag_rule.enterprise + end + can [:admin, :index, :create], Enterprise can [:read, :edit, :update, :bulk_update, :resend_confirmation], Enterprise do |enterprise| OpenFoodNetwork::Permissions.new(user).editable_enterprises.include? enterprise diff --git a/app/views/admin/enterprises/form/_tag_rules.html.haml b/app/views/admin/enterprises/form/_tag_rules.html.haml index ecb8b54f2e..9a3c0f7a40 100644 --- a/app/views/admin/enterprises/form/_tag_rules.html.haml +++ b/app/views/admin/enterprises/form/_tag_rules.html.haml @@ -5,17 +5,28 @@ No tags apply to this enterprise yet .customer_tag{ ng: { repeat: "tagGroup in tagGroups" }, bindonce: true } .header - %h3 - For customers tagged: - %tags-input{ ng: { model: 'tagGroup.tags'}, - min: { tags: "1" }, - on: { tag: { added: "updateTagsRulesFor(tagGroup)", removed: "updateTagsRulesFor(tagGroup)" } } } + %table + %colgroup + %col{width: '35%'} + %col{width: '65%'} + %tr + %td + %h5 + For customers tagged: + %td + %tags-input{ ng: { model: 'tagGroup.tags'}, + min: { tags: "1" }, + on: { tag: { added: "updateTagsRulesFor(tagGroup)", removed: "updateTagsRulesFor(tagGroup)" } } } .no_rules{ ng: { show: "tagGroup.rules.length == 0" } } No rules apply to this tag yet - .tag_rule{ ng: { repeat: "rule in tagGroup.rules" } } - %discount-order{ bo: { if: "rule.type == 'TagRule::DiscountOrder'" } } - .add_rule + %table + %tr.tag_rule{ id: "tr_{{rule.id}}", ng: { repeat: "rule in tagGroup.rules" } } + %td + %discount-order{ bo: { if: "rule.type == 'TagRule::DiscountOrder'" } } + %td.actions + %a{ ng: { click: "deleteTagRule(tagGroup, rule)" }, :class => "delete-tag-rule icon-trash no-text" } + .add_rule.text-center %input.button.icon-plus{ type: 'button', value: "+ Add A New Rule", ng: { click: 'addNewRuleTo(tagGroup)' } } - .add_tag - %input.button.icon-plus{ type: 'button', value: "+ Add A New Tag", ng: { click: 'addNewTag()' } } + .add_tage + %input.button.red.icon-plus{ type: 'button', value: "+ Add A New Tag", ng: { click: 'addNewTag()' } } diff --git a/config/routes.rb b/config/routes.rb index c314747786..26d675838c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -87,6 +87,8 @@ Openfoodnetwork::Application.routes.draw do resources :producer_properties do post :update_positions, on: :collection end + + resources :tag_rules, only: [:destroy] end resources :enterprise_relationships diff --git a/spec/controllers/admin/tag_rules_controller_spec.rb b/spec/controllers/admin/tag_rules_controller_spec.rb new file mode 100644 index 0000000000..fa95650479 --- /dev/null +++ b/spec/controllers/admin/tag_rules_controller_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Admin::TagRulesController, type: :controller do + + describe "destroy" do + context "json" do + let(:format) { :json } + + let(:enterprise) { create(:distributor_enterprise) } + let!(:tag_rule) { create(:tag_rule, enterprise: enterprise) } + let(:params) { { format: format, id: tag_rule.id } } + + context "where I don't manage the tag rule enterprise" do + let(:user) { create(:user) } + + before do + user.owned_enterprises << create(:enterprise) + allow(controller).to receive(:spree_current_user) { user } + end + + it "redirects to unauthorized" do + spree_delete :destroy, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "where I manage the tag rule enterprise" do + before do + allow(controller).to receive(:spree_current_user) { enterprise.owner } + end + + it { expect{ spree_delete :destroy, params }.to change{TagRule.count}.by(-1) } + end + end + end +end diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 2b77d6e18b..cc923cd89e 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -334,6 +334,28 @@ feature %q{ expect(tag_rule.calculator.preferred_flat_percent).to eq 45 end end + + context "deleting" do + let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } + + before do + login_to_admin_section + visit main_app.edit_admin_enterprise_path(enterprise) + end + + it "deletes rules from the database" do + click_link "Tag Rules" + + expect(page).to have_selector "#tr_#{tag_rule.id}" + + expect{ + within "#tr_#{tag_rule.id}" do + first("a.delete-tag-rule").click + end + expect(page).to_not have_selector "#tr_#{tag_rule.id}" + }.to change{TagRule.count}.by(-1) + end + end end diff --git a/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee index dd2fcee449..988040694c 100644 --- a/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee @@ -6,6 +6,7 @@ describe "TagRulesCtrl", -> beforeEach -> module('admin.enterprises') enterprise = + id: 45 tag_groups: [ { tags: "member", rules: [{ id: 1, preferred_customer_tags: "member" }, { id: 2, preferred_customer_tags: "member" }] }, { tags: "volunteer", rules: [{ id: 3, preferred_customer_tags: "local" }] } @@ -21,7 +22,58 @@ describe "TagRulesCtrl", -> expect(scope.tagGroups[0].startIndex).toEqual 0 expect(scope.tagGroups[1].startIndex).toEqual 2 - it "updates when tags are added to a tagGroup", -> + describe "adding a new tag group", -> + beforeEach -> scope.addNewRuleTo(scope.tagGroups[0]) + + it "adds a new rule to the rules array for the tagGroup", -> + expect(scope.tagGroups[0].rules.length).toEqual 3 + + it "updates tagGroup start indices", -> expect(scope.tagGroups[0].startIndex).toEqual 0 expect(scope.tagGroups[1].startIndex).toEqual 3 + + describe "deleting a tag group", -> + describe "where the rule is not in the rule list for the tagGroup", -> + beforeEach -> + scope.deleteTagRule(scope.tagGroups[0],scope.tagGroups[1].rules[0]) + + it "does not remove any rules", -> + expect(scope.tagGroups[0].rules.length).toEqual 2 + expect(scope.tagGroups[1].rules.length).toEqual 1 + + describe "with an id", -> + rule = null + + beforeEach inject ($httpBackend) -> + rule = scope.tagGroups[0].rules[0] + spyOn(window, "confirm").andReturn(true) + $httpBackend.expectDELETE('/admin/enterprises/45/tag_rules/1.json').respond(status: 204) + scope.deleteTagRule(scope.tagGroups[0], rule) + $httpBackend.flush() + + it "removes the specified rule from the rules list", -> + expect(scope.tagGroups[0].rules.length).toEqual 1 + expect(scope.tagGroups[1].rules.length).toEqual 1 + expect(scope.tagGroups[0].rules.indexOf(rule)).toEqual -1 + + it "updates tagGroup start indices", -> + expect(scope.tagGroups[0].startIndex).toEqual 0 + expect(scope.tagGroups[1].startIndex).toEqual 1 + + describe "without an id", -> + rule = null + + beforeEach inject ($httpBackend) -> + rule = scope.tagGroups[0].rules[0] + rule.id = null + scope.deleteTagRule(scope.tagGroups[0], rule) + + it "removes the specified rule from the rules list", -> + expect(scope.tagGroups[0].rules.length).toEqual 1 + expect(scope.tagGroups[1].rules.length).toEqual 1 + expect(scope.tagGroups[0].rules.indexOf(rule)).toEqual -1 + + it "updates tagGroup start indices", -> + expect(scope.tagGroups[0].startIndex).toEqual 0 + expect(scope.tagGroups[1].startIndex).toEqual 1 From f902474591fe516aeefdda108710236a82761ba6 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 16 Mar 2016 18:00:16 +1100 Subject: [PATCH 166/215] Relevant DiscountOrder tag rules are applied to orders on update --- .../directives/invert_number.js.coffee | 11 ++ .../admin/tag_rules/discount_order.html.haml | 9 +- app/models/spree/order_decorator.rb | 25 ++- app/models/tag_rule.rb | 3 +- app/models/tag_rule/discount_order.rb | 4 +- config/locales/en.yml | 2 +- spec/features/admin/enterprises_spec.rb | 10 +- spec/models/spree/order_spec.rb | 159 +++++++++++++----- spec/models/tag_rule/discount_order_spec.rb | 4 +- 9 files changed, 159 insertions(+), 68 deletions(-) create mode 100644 app/assets/javascripts/admin/enterprises/directives/invert_number.js.coffee diff --git a/app/assets/javascripts/admin/enterprises/directives/invert_number.js.coffee b/app/assets/javascripts/admin/enterprises/directives/invert_number.js.coffee new file mode 100644 index 0000000000..0557d3ed22 --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/directives/invert_number.js.coffee @@ -0,0 +1,11 @@ +angular.module("admin.enterprises").directive "invertNumber", -> + restrict: "A" + require: "ngModel" + link: (scope, element, attrs, ngModel) -> + ngModel.$parsers.push (viewValue) -> + return -parseInt(viewValue) unless isNaN(parseInt(viewValue)) + viewValue + + ngModel.$formatters.push (modelValue) -> + return -parseInt(modelValue) unless isNaN(parseInt(modelValue)) + modelValue diff --git a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml index bc2ddc5c75..90886b2dab 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml @@ -24,13 +24,16 @@ name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][calculator_attributes][id]", ng: { value: "rule.calculator.id" } } + %input{ type: "hidden", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][calculator_attributes][preferred_flat_percent]", + ng: { value: "rule.calculator.preferred_flat_percent" } } + %span.text-normal {{ $index + 1 }}. Apply a discount of %span.input-symbol.after %span.text-normal % %input.text-big{ type: "number", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][calculator_attributes][preferred_flat_percent]", - min: 0, + min: -100, max: 100, - ng: { model: "rule.calculator.preferred_flat_percent" } } + ng: { model: "rule.calculator.preferred_flat_percent" }, 'invert-number' => true } %span.text-normal to order subtotals diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 8f1c0381b1..5fc96c34c2 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -17,7 +17,8 @@ Spree::Order.class_eval do attr_accessible :order_cycle_id, :distributor_id before_validation :shipping_address_from_distributor - before_validation :associate_customer, unless: :customer_is_valid? + before_validation :associate_customer, unless: :customer_id? + before_validation :ensure_customer, unless: :customer_is_valid? checkout_flow do go_to_state :address @@ -179,6 +180,10 @@ Spree::Order.class_eval do if order_cycle OpenFoodNetwork::EnterpriseFeeCalculator.new.create_order_adjustments_for self end + + if distributor.present? && customer.present? + distributor.apply_tag_rules_to(self, customer: customer) + end end end @@ -289,14 +294,18 @@ Spree::Order.class_eval do customer.present? && customer.enterprise_id == distributor_id && customer.email == (user.andand.email || email) end + def email_for_customer + user.andand.email || email + end + def associate_customer - email_for_customer = user.andand.email || email - existing_customer = Customer.of(distributor).find_by_email(email_for_customer) - if existing_customer - self.customer = existing_customer - else - new_customer = Customer.create(enterprise: distributor, email: email_for_customer, user: user) - self.customer = new_customer + return customer if customer.present? + self.customer = Customer.of(distributor).find_by_email(email_for_customer) + end + + def ensure_customer + unless associate_customer + self.customer = Customer.create(enterprise: distributor, email: email_for_customer, user: user) end end end diff --git a/app/models/tag_rule.rb b/app/models/tag_rule.rb index cf0a105e80..0237b74eb2 100644 --- a/app/models/tag_rule.rb +++ b/app/models/tag_rule.rb @@ -31,6 +31,7 @@ class TagRule < ActiveRecord::Base def customer_tags_match? context_customer_tags = context.andand[:customer].andand.tag_list || [] - ( context_customer_tags & preferred_customer_tags.split(",") ).any? + preferred_tags = preferred_customer_tags.split(",") + ( context_customer_tags & preferred_tags ).any? end end diff --git a/app/models/tag_rule/discount_order.rb b/app/models/tag_rule/discount_order.rb index 1ee9b4263a..c1e440760a 100644 --- a/app/models/tag_rule/discount_order.rb +++ b/app/models/tag_rule/discount_order.rb @@ -5,9 +5,7 @@ class TagRule::DiscountOrder < TagRule # Warning: this should only EVER be called via TagRule#apply def apply! - percentage = "%.2f" % (calculator.preferred_flat_percent * -1) - label = I18n.t("tag_rules.discount_order.label", percentage: percentage) - create_adjustment(label, subject, subject) + create_adjustment(I18n.t("tag_rules.discount_order.discount"), subject, subject) end def subject_class diff --git a/config/locales/en.yml b/config/locales/en.yml index 7892ecdce1..4a9d8fe546 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -140,7 +140,7 @@ en: # Tag Rules tag_rules: discount_order: - label: "%{percentage}% discount" + discount: "Discount" logo: "Logo (640x130)" #FIXME logo_mobile: "Mobile logo (75x26)" #FIXME diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index cc923cd89e..a39a3b8acb 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -301,13 +301,13 @@ feature %q{ expect(page).to have_content 'No rules apply to this tag yet' click_button '+ Add A New Rule' - fill_in "enterprise[tag_rules_attributes][0][calculator_attributes][preferred_flat_percent]", with: 22 + fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 22 click_button 'Update' tag_rule = TagRule::DiscountOrder.last expect(tag_rule.preferred_customer_tags).to eq "volunteer" - expect(tag_rule.calculator.preferred_flat_percent).to eq 22 + expect(tag_rule.calculator.preferred_flat_percent).to eq -22 end end @@ -325,13 +325,13 @@ feature %q{ expect(first('.customer_tag .header')).to have_content "For customers tagged:" expect(first('tags-input .tag-list ti-tag-item')).to have_content "member" find(:css, "tags-input .tags input").set "volunteer\n" - expect(page).to have_input "enterprise[tag_rules_attributes][0][calculator_attributes][preferred_flat_percent]", with: "0" - fill_in "enterprise[tag_rules_attributes][0][calculator_attributes][preferred_flat_percent]", with: 45 + expect(page).to have_field "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: '0' + fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 45 click_button 'Update' expect(tag_rule.preferred_customer_tags).to eq "member,volunteer" - expect(tag_rule.calculator.preferred_flat_percent).to eq 45 + expect(tag_rule.calculator.preferred_flat_percent).to eq -45 end end diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 4d681e01cc..e72fc7a1c4 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -108,44 +108,76 @@ describe Spree::Order do subject.update_distribution_charge! end - describe "looking up whether a line item can be provided by an order cycle" do - it "returns true when the variant is provided" do - v = double(:variant) - line_item = double(:line_item, variant: v) - order_cycle = double(:order_cycle, variants: [v]) - subject.stub(:order_cycle) { order_cycle } + context "appying tag rules" do + let(:enterprise) { create(:distributor_enterprise) } + let(:customer) { create(:customer, enterprise: enterprise, tag_list: "tagtagtag") } + let(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "tagtagtag") } + let(:order) { create(:order_with_totals_and_distribution, distributor: enterprise, customer: customer) } - subject.send(:provided_by_order_cycle?, line_item).should be_true + before do + tag_rule.calculator.update_attribute(:preferred_flat_percent, -10) end - it "returns false otherwise" do - v = double(:variant) - line_item = double(:line_item, variant: v) - order_cycle = double(:order_cycle, variants: []) - subject.stub(:order_cycle) { order_cycle } - - subject.send(:provided_by_order_cycle?, line_item).should be_false + context "when the rule applies" do + it "applies the rule" do + order.update_distribution_charge! + order.reload + discount = order.adjustments.find_by_label("Discount") + expect(discount).to be_a Spree::Adjustment + expect(discount.amount).to eq (order.item_total / -10).round(2) + end end - it "returns false when there is no order cycle" do - v = double(:variant) - line_item = double(:line_item, variant: v) - subject.stub(:order_cycle) { nil } + context "when the rule does not apply" do + before { tag_rule.update_attribute(:preferred_customer_tags, "tagtag") } - subject.send(:provided_by_order_cycle?, line_item).should be_false + it "does not apply the rule" do + order.update_distribution_charge! + order.reload + discount = order.adjustments.find_by_label("Discount") + expect(discount).to be_nil + end end end + end - it "looks up product distribution enterprise fees for a line item" do - product = double(:product) - variant = double(:variant, product: product) - line_item = double(:line_item, variant: variant) + describe "looking up whether a line item can be provided by an order cycle" do + it "returns true when the variant is provided" do + v = double(:variant) + line_item = double(:line_item, variant: v) + order_cycle = double(:order_cycle, variants: [v]) + subject.stub(:order_cycle) { order_cycle } - product_distribution = double(:product_distribution) - product.should_receive(:product_distribution_for).with(subject.distributor) { product_distribution } - - subject.send(:product_distribution_for, line_item).should == product_distribution + subject.send(:provided_by_order_cycle?, line_item).should be_true end + + it "returns false otherwise" do + v = double(:variant) + line_item = double(:line_item, variant: v) + order_cycle = double(:order_cycle, variants: []) + subject.stub(:order_cycle) { order_cycle } + + subject.send(:provided_by_order_cycle?, line_item).should be_false + end + + it "returns false when there is no order cycle" do + v = double(:variant) + line_item = double(:line_item, variant: v) + subject.stub(:order_cycle) { nil } + + subject.send(:provided_by_order_cycle?, line_item).should be_false + end + end + + it "looks up product distribution enterprise fees for a line item" do + product = double(:product) + variant = double(:variant, product: product) + line_item = double(:line_item, variant: variant) + + product_distribution = double(:product_distribution) + product.should_receive(:product_distribution_for).with(subject.distributor) { product_distribution } + + subject.send(:product_distribution_for, line_item).should == product_distribution end describe "getting the admin and handling charge" do @@ -559,39 +591,76 @@ describe Spree::Order do end describe "associating a customer" do - let(:user) { create(:user) } let(:distributor) { create(:distributor_enterprise) } + let!(:order) { create(:order, distributor: distributor) } - context "when a user has been set on the order" do - let!(:order) { create(:order, distributor: distributor, user: user) } - context "and a customer for order.distributor and order.user.email already exists" do - let!(:customer) { create(:customer, enterprise: distributor, email: user.email) } - it "associates the order with the existing customer" do - order.send(:associate_customer) + context "when an email address is available for the order" do + before { allow(order).to receive(:email_for_customer) { "existing@email.com" }} + + context "and a customer for order.distributor and order#email_for_customer already exists" do + let!(:customer) { create(:customer, enterprise: distributor, email: "existing@email.com" ) } + + it "associates the order with the existing customer, and returns the customer" do + result = order.send(:associate_customer) expect(order.customer).to eq customer + expect(result).to eq customer end end + context "and a customer for order.distributor and order.user.email does not alread exist" do let!(:customer) { create(:customer, enterprise: distributor, email: 'some-other-email@email.com') } - it "creates a new customer" do - expect{order.send(:associate_customer)}.to change{Customer.count}.by 1 + + it "does not set the customer and returns nil" do + result = order.send(:associate_customer) + expect(order.customer).to be_nil + expect(result).to be_nil end end end - context "when a user has not been set on the order" do - let!(:order) { create(:order, distributor: distributor, user: nil) } - context "and a customer for order.distributor and order.email already exists" do - let!(:customer) { create(:customer, enterprise: distributor, email: order.email) } - it "creates a new customer" do - order.send(:associate_customer) + context "when an email address is not available for the order" do + let!(:customer) { create(:customer, enterprise: distributor) } + before { allow(order).to receive(:email_for_customer) { nil }} + + it "does not set the customer and returns nil" do + result = order.send(:associate_customer) + expect(order.customer).to be_nil + expect(result).to be_nil + end + end + end + + describe "ensuring a customer is linked" do + let(:distributor) { create(:distributor_enterprise) } + let!(:order) { create(:order, distributor: distributor) } + + context "when a customer has already been linked to the order" do + let!(:customer) { create(:customer, enterprise: distributor, email: "existing@email.com" ) } + before { order.update_attribute(:customer_id, customer.id) } + + it "does nothing" do + order.send(:ensure_customer) + expect(order.customer).to eq customer + end + end + + context "when a customer not been linked to the order" do + context "but one matching order#email_for_customer already exists" do + let!(:customer) { create(:customer, enterprise: distributor, email: 'some-other-email@email.com') } + before { allow(order).to receive(:email_for_customer) { 'some-other-email@email.com' } } + + it "links the customer customer to the order" do + expect(order.customer).to be_nil + expect{order.send(:ensure_customer)}.to_not change{Customer.count} expect(order.customer).to eq customer end end - context "and a customer for order.distributor and order.email does not alread exist" do - let!(:customer) { create(:customer, enterprise: distributor, email: 'some-other-email@email.com') } + + context "and order#email_for_customer does not match any existing customers" do it "creates a new customer" do - expect{order.send(:associate_customer)}.to change{Customer.count}.by 1 + expect(order.customer).to be_nil + expect{order.send(:ensure_customer)}.to change{Customer.count}.by 1 + expect(order.customer).to be_a Customer end end end diff --git a/spec/models/tag_rule/discount_order_spec.rb b/spec/models/tag_rule/discount_order_spec.rb index 32f2de8874..dab901dfbf 100644 --- a/spec/models/tag_rule/discount_order_spec.rb +++ b/spec/models/tag_rule/discount_order_spec.rb @@ -66,7 +66,7 @@ describe TagRule::DiscountOrder, type: :model do tag_rule.send(:apply!) expect(adjustment).to be_a Spree::Adjustment expect(adjustment.amount).to eq -10.00 - expect(adjustment.label).to eq "10.00% discount" + expect(adjustment.label).to eq "Discount" expect(order.adjustment_total).to eq -10.00 expect(order.total).to eq 90.00 end @@ -84,7 +84,7 @@ describe TagRule::DiscountOrder, type: :model do tag_rule.send(:apply!) expect(adjustment).to be_a Spree::Adjustment expect(adjustment.amount).to eq -10.00 - expect(adjustment.label).to eq "10.00% discount" + expect(adjustment.label).to eq "Discount" expect(order.adjustment_total).to eq 15.00 expect(order.total).to eq 115.00 end From c74c274a9ebb2175af4e7e0b2754313d21170f75 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 17 Mar 2016 11:05:43 +1100 Subject: [PATCH 167/215] Renaming shippingMethodCtrl to shippingMethodsCtrl --- .../controllers/shipping_method_controller.js.coffee | 4 ---- .../controllers/shipping_methods_controller.js.coffee | 4 ++++ app/views/admin/enterprises/form/_shipping_methods.html.haml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 app/assets/javascripts/admin/shipping_methods/controllers/shipping_method_controller.js.coffee create mode 100644 app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee diff --git a/app/assets/javascripts/admin/shipping_methods/controllers/shipping_method_controller.js.coffee b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_method_controller.js.coffee deleted file mode 100644 index dabe52574e..0000000000 --- a/app/assets/javascripts/admin/shipping_methods/controllers/shipping_method_controller.js.coffee +++ /dev/null @@ -1,4 +0,0 @@ -angular.module("admin.shipping_methods") - .controller "shippingMethodCtrl", ($scope, ShippingMethods) -> - $scope.findShippingMethodByID = (id) -> - $scope.ShippingMethod = ShippingMethods.findByID(id) \ No newline at end of file diff --git a/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee new file mode 100644 index 0000000000..aac83f8cc1 --- /dev/null +++ b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee @@ -0,0 +1,4 @@ +angular.module("admin.shipping_methods") + .controller "shippingMethodsCtrl", ($scope, ShippingMethods) -> + $scope.findShippingMethodByID = (id) -> + $scope.ShippingMethod = ShippingMethods.findByID(id) diff --git a/app/views/admin/enterprises/form/_shipping_methods.html.haml b/app/views/admin/enterprises/form/_shipping_methods.html.haml index 445b026c38..5ccc22a720 100644 --- a/app/views/admin/enterprises/form/_shipping_methods.html.haml +++ b/app/views/admin/enterprises/form/_shipping_methods.html.haml @@ -7,7 +7,7 @@ %th %tbody - @shipping_methods.each do |shipping_method| - %tr{ ng: { controller: 'shippingMethodCtrl', init: "findShippingMethodByID(#{shipping_method.id})" } } + %tr{ ng: { controller: 'shippingMethodsCtrl', init: "findShippingMethodByID(#{shipping_method.id})" } } %td= shipping_method.name %td= f.check_box :shipping_method_ids, { :multiple => true, 'ng-model' => 'ShippingMethod.selected' }, shipping_method.id, nil %td= link_to "Edit", edit_admin_shipping_method_path(shipping_method) From 443e232ea4376bc126cd9ffab96cbaf6266eea28 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 17 Mar 2016 11:11:47 +1100 Subject: [PATCH 168/215] Renaming angular admin.shipping_methods and admin.payment_methods modules --- .../javascripts/admin/enterprises/enterprises.js.coffee | 2 +- .../controllers/payment_method_controller.js.coffee | 4 ++-- .../admin/payment_methods/payment_methods.js.coffee | 2 +- .../admin/payment_methods/services/payment_methods.js.coffee | 2 +- .../controllers/shipping_methods_controller.js.coffee | 2 +- .../shipping_methods/services/shipping_methods.js.coffee | 2 +- .../admin/shipping_methods/shipping_methods.js.coffee | 2 +- app/helpers/admin/injection_helper.rb | 4 ++-- .../_form/replace_form_fields.html.haml.deface | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee index 4537620512..4adb31cf08 100644 --- a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee +++ b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee @@ -1 +1 @@ -angular.module("admin.enterprises", [ "admin.payment_methods", "admin.utils", "admin.shipping_methods", "admin.users", "textAngular", "admin.side_menu", "admin.taxons", 'admin.indexUtils', 'admin.dropdown', 'pasvaz.bindonce', 'ngSanitize', 'ngTagsInput'] ) \ No newline at end of file +angular.module("admin.enterprises", [ "admin.paymentMethods", "admin.utils", "admin.shippingMethods", "admin.users", "textAngular", "admin.side_menu", "admin.taxons", 'admin.indexUtils', 'admin.dropdown', 'pasvaz.bindonce', 'ngSanitize', 'ngTagsInput'] ) \ No newline at end of file diff --git a/app/assets/javascripts/admin/payment_methods/controllers/payment_method_controller.js.coffee b/app/assets/javascripts/admin/payment_methods/controllers/payment_method_controller.js.coffee index 092fd5bbd2..c2595faa6d 100644 --- a/app/assets/javascripts/admin/payment_methods/controllers/payment_method_controller.js.coffee +++ b/app/assets/javascripts/admin/payment_methods/controllers/payment_method_controller.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.payment_methods") +angular.module("admin.paymentMethods") .controller "paymentMethodCtrl", ($scope, PaymentMethods) -> $scope.findPaymentMethodByID = (id) -> - $scope.PaymentMethod = PaymentMethods.findByID(id) \ No newline at end of file + $scope.PaymentMethod = PaymentMethods.findByID(id) diff --git a/app/assets/javascripts/admin/payment_methods/payment_methods.js.coffee b/app/assets/javascripts/admin/payment_methods/payment_methods.js.coffee index e75142ae0d..01553647d4 100644 --- a/app/assets/javascripts/admin/payment_methods/payment_methods.js.coffee +++ b/app/assets/javascripts/admin/payment_methods/payment_methods.js.coffee @@ -1 +1 @@ -angular.module("admin.payment_methods", []) \ No newline at end of file +angular.module("admin.paymentMethods", []) diff --git a/app/assets/javascripts/admin/payment_methods/services/payment_methods.js.coffee b/app/assets/javascripts/admin/payment_methods/services/payment_methods.js.coffee index 21e557cac3..c31a20d96f 100644 --- a/app/assets/javascripts/admin/payment_methods/services/payment_methods.js.coffee +++ b/app/assets/javascripts/admin/payment_methods/services/payment_methods.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.payment_methods") +angular.module("admin.paymentMethods") .factory "PaymentMethods", (paymentMethods) -> new class PaymentMethods paymentMethods: paymentMethods diff --git a/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee index aac83f8cc1..91569b2256 100644 --- a/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee +++ b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.shipping_methods") +angular.module("admin.shippingMethods") .controller "shippingMethodsCtrl", ($scope, ShippingMethods) -> $scope.findShippingMethodByID = (id) -> $scope.ShippingMethod = ShippingMethods.findByID(id) diff --git a/app/assets/javascripts/admin/shipping_methods/services/shipping_methods.js.coffee b/app/assets/javascripts/admin/shipping_methods/services/shipping_methods.js.coffee index 556445c869..c691f5dae5 100644 --- a/app/assets/javascripts/admin/shipping_methods/services/shipping_methods.js.coffee +++ b/app/assets/javascripts/admin/shipping_methods/services/shipping_methods.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.shipping_methods") +angular.module("admin.shippingMethods") .factory "ShippingMethods", (shippingMethods) -> new class ShippingMethods shippingMethods: shippingMethods diff --git a/app/assets/javascripts/admin/shipping_methods/shipping_methods.js.coffee b/app/assets/javascripts/admin/shipping_methods/shipping_methods.js.coffee index 99aeb9566d..01e989a104 100644 --- a/app/assets/javascripts/admin/shipping_methods/shipping_methods.js.coffee +++ b/app/assets/javascripts/admin/shipping_methods/shipping_methods.js.coffee @@ -1 +1 @@ -angular.module("admin.shipping_methods", []) \ No newline at end of file +angular.module("admin.shippingMethods", []) diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 511eced707..36463800bc 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -20,11 +20,11 @@ module Admin end def admin_inject_payment_methods - admin_inject_json_ams_array "admin.payment_methods", "paymentMethods", @payment_methods, Api::Admin::IdNameSerializer + admin_inject_json_ams_array "admin.paymentMethods", "paymentMethods", @payment_methods, Api::Admin::IdNameSerializer end def admin_inject_shipping_methods - admin_inject_json_ams_array "admin.shipping_methods", "shippingMethods", @shipping_methods, Api::Admin::IdNameSerializer + admin_inject_json_ams_array "admin.shippingMethods", "shippingMethods", @shipping_methods, Api::Admin::IdNameSerializer end def admin_inject_shops(ngModule='admin.customers') diff --git a/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface b/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface index 190cff1a17..3ec441449e 100644 --- a/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface +++ b/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface @@ -48,4 +48,4 @@ .row .alpha.eleven.columns - = render :partial => 'spree/admin/shared/calculator_fields', :locals => { :f => f } \ No newline at end of file + = render :partial => 'spree/admin/shared/calculator_fields', :locals => { :f => f } From 86c80124fcca0ee0be63a11fb1a82580fd89f9f7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 17 Mar 2016 11:47:32 +1100 Subject: [PATCH 169/215] Moving tags-with-translation directive to admin.utils --- app/assets/javascripts/admin/customers/customers.js.coffee | 2 +- .../directives/tags_with_translation.js.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/assets/javascripts/admin/{customers => utils}/directives/tags_with_translation.js.coffee (78%) diff --git a/app/assets/javascripts/admin/customers/customers.js.coffee b/app/assets/javascripts/admin/customers/customers.js.coffee index 3733fe2eea..1e8ae9b988 100644 --- a/app/assets/javascripts/admin/customers/customers.js.coffee +++ b/app/assets/javascripts/admin/customers/customers.js.coffee @@ -1 +1 @@ -angular.module("admin.customers", ['ngResource', 'ngTagsInput', 'admin.indexUtils', 'admin.dropdown']) \ No newline at end of file +angular.module("admin.customers", ['ngResource', 'ngTagsInput', 'admin.indexUtils', 'admin.utils', 'admin.dropdown']) \ No newline at end of file diff --git a/app/assets/javascripts/admin/customers/directives/tags_with_translation.js.coffee b/app/assets/javascripts/admin/utils/directives/tags_with_translation.js.coffee similarity index 78% rename from app/assets/javascripts/admin/customers/directives/tags_with_translation.js.coffee rename to app/assets/javascripts/admin/utils/directives/tags_with_translation.js.coffee index e15ec10342..eae1a899c1 100644 --- a/app/assets/javascripts/admin/customers/directives/tags_with_translation.js.coffee +++ b/app/assets/javascripts/admin/utils/directives/tags_with_translation.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.customers").directive "tagsWithTranslation", -> +angular.module("admin.utils").directive "tagsWithTranslation", -> restrict: "E" template: "" scope: From f9acee2be428fa6f5fcebec7bc511f45897a1f3d Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 17 Mar 2016 13:33:14 +1100 Subject: [PATCH 170/215] Shipping methods can be tagged --- .../controllers/shipping_method_controller.js.coffee | 2 ++ .../shipping_methods/shipping_methods.js.coffee | 2 +- app/helpers/admin/injection_helper.rb | 4 ++++ app/models/spree/shipping_method_decorator.rb | 4 +++- .../_form/replace_form_fields.html.haml.deface | 12 +++++++++++- .../api/admin/shipping_method_serializer.rb | 7 +++++++ spec/features/admin/shipping_methods_spec.rb | 4 ++++ 7 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/admin/shipping_methods/controllers/shipping_method_controller.js.coffee create mode 100644 app/serializers/api/admin/shipping_method_serializer.rb diff --git a/app/assets/javascripts/admin/shipping_methods/controllers/shipping_method_controller.js.coffee b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_method_controller.js.coffee new file mode 100644 index 0000000000..cc7bd4ee3e --- /dev/null +++ b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_method_controller.js.coffee @@ -0,0 +1,2 @@ +angular.module("admin.shippingMethods").controller "shippingMethodCtrl", ($scope, shippingMethod) -> + $scope.shippingMethod = shippingMethod diff --git a/app/assets/javascripts/admin/shipping_methods/shipping_methods.js.coffee b/app/assets/javascripts/admin/shipping_methods/shipping_methods.js.coffee index 01e989a104..232eee7045 100644 --- a/app/assets/javascripts/admin/shipping_methods/shipping_methods.js.coffee +++ b/app/assets/javascripts/admin/shipping_methods/shipping_methods.js.coffee @@ -1 +1 @@ -angular.module("admin.shippingMethods", []) +angular.module("admin.shippingMethods", ["ngTagsInput", 'admin.utils']) diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 36463800bc..0c032eaa9e 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -27,6 +27,10 @@ module Admin admin_inject_json_ams_array "admin.shippingMethods", "shippingMethods", @shipping_methods, Api::Admin::IdNameSerializer end + def admin_inject_shipping_method + admin_inject_json_ams "admin.shippingMethods", "shippingMethod", @shipping_method, Api::Admin::ShippingMethodSerializer + end + def admin_inject_shops(ngModule='admin.customers') admin_inject_json_ams_array ngModule, "shops", @shops, Api::Admin::IdNameSerializer end diff --git a/app/models/spree/shipping_method_decorator.rb b/app/models/spree/shipping_method_decorator.rb index b8be603048..f4f1d2c999 100644 --- a/app/models/spree/shipping_method_decorator.rb +++ b/app/models/spree/shipping_method_decorator.rb @@ -1,10 +1,12 @@ Spree::ShippingMethod.class_eval do + acts_as_taggable + has_many :distributor_shipping_methods has_many :distributors, through: :distributor_shipping_methods, class_name: 'Enterprise', foreign_key: 'distributor_id' after_save :touch_distributors attr_accessible :distributor_ids, :description - attr_accessible :require_ship_address + attr_accessible :require_ship_address, :tag_list validates :distributors, presence: { message: "^At least one hub must be selected" } diff --git a/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface b/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface index 3ec441449e..a2f9ed766a 100644 --- a/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface +++ b/app/overrides/spree/admin/shipping_methods/_form/replace_form_fields.html.haml.deface @@ -1,6 +1,9 @@ / replace "div[data-hook='admin_shipping_method_form_fields']" -.alpha.eleven.columns{"data-hook" => "admin_shipping_method_form_fields"} +=admin_inject_shipping_method +.alpha.eleven.columns{ "data-hook" => "admin_shipping_method_form_fields", + "ng-app" => "admin.shippingMethods", + "ng-controller" => "shippingMethodCtrl" } .row .alpha.three.columns = f.label :name, t(:name) @@ -46,6 +49,13 @@   = f.label :pick_up, t(:pick_up) + .row + .alpha.three.columns + = f.label :tags, t(:tags) + .omega.eight.columns + = f.hidden_field :tag_list, "ng-value" => "shippingMethod.tag_list" + %tags-with-translation#something{ object: "shippingMethod" } + .row .alpha.eleven.columns = render :partial => 'spree/admin/shared/calculator_fields', :locals => { :f => f } diff --git a/app/serializers/api/admin/shipping_method_serializer.rb b/app/serializers/api/admin/shipping_method_serializer.rb new file mode 100644 index 0000000000..9fbb864d09 --- /dev/null +++ b/app/serializers/api/admin/shipping_method_serializer.rb @@ -0,0 +1,7 @@ +class Api::Admin::ShippingMethodSerializer < ActiveModel::Serializer + attributes :id, :name, :tags + + def tags + object.tag_list.map{ |t| { text: t } } + end +end diff --git a/spec/features/admin/shipping_methods_spec.rb b/spec/features/admin/shipping_methods_spec.rb index 9f04b6707b..646b383c21 100644 --- a/spec/features/admin/shipping_methods_spec.rb +++ b/spec/features/admin/shipping_methods_spec.rb @@ -93,12 +93,16 @@ feature 'shipping methods' do fill_in 'shipping_method_name', :with => 'Teleport' check "shipping_method_distributor_ids_#{distributor1.id}" + find(:css, "tags-input .tags input").set "local\n" + click_button 'Create' flash_message.should == 'Shipping method "Teleport" has been successfully created!' + expect(first('tags-input .tag-list ti-tag-item')).to have_content "local" shipping_method = Spree::ShippingMethod.find_by_name('Teleport') shipping_method.distributors.should == [distributor1] + shipping_method.tag_list.should == ["local"] end it "shows me only shipping methods I have access to" do From 3d78b375c62c431ceb712e8a5f201eb91e165848 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 17 Mar 2016 15:24:08 +1100 Subject: [PATCH 171/215] Moving angularjs tag rule logic into its own module --- app/assets/javascripts/admin/all.js | 1 + .../javascripts/admin/enterprises/enterprises.js.coffee | 2 +- .../controllers/tag_rules_controller.js.coffee | 6 +++--- .../directives/invert_number.js.coffee | 2 +- .../directives/tag_rules/discount_order.js.coffee | 2 +- app/assets/javascripts/admin/tag_rules/tag_rules.js.coffee | 1 + .../controllers/tag_rules_controller_spec.js.coffee | 5 ++--- 7 files changed, 10 insertions(+), 9 deletions(-) rename app/assets/javascripts/admin/{enterprises => tag_rules}/controllers/tag_rules_controller.js.coffee (82%) rename app/assets/javascripts/admin/{enterprises => tag_rules}/directives/invert_number.js.coffee (83%) rename app/assets/javascripts/admin/{enterprises => tag_rules}/directives/tag_rules/discount_order.js.coffee (56%) create mode 100644 app/assets/javascripts/admin/tag_rules/tag_rules.js.coffee rename spec/javascripts/unit/admin/{enterprises => tag_rules}/controllers/tag_rules_controller_spec.js.coffee (95%) diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 1e2b6f3898..9f99dc1dcd 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -38,6 +38,7 @@ //= require ./products/products //= require ./shipping_methods/shipping_methods //= require ./side_menu/side_menu +//= require ./tag_rules/tag_rules //= require ./taxons/taxons //= require ./utils/utils //= require ./users/users diff --git a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee index 4adb31cf08..2074a1ea05 100644 --- a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee +++ b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee @@ -1 +1 @@ -angular.module("admin.enterprises", [ "admin.paymentMethods", "admin.utils", "admin.shippingMethods", "admin.users", "textAngular", "admin.side_menu", "admin.taxons", 'admin.indexUtils', 'admin.dropdown', 'pasvaz.bindonce', 'ngSanitize', 'ngTagsInput'] ) \ No newline at end of file +angular.module("admin.enterprises", [ "admin.paymentMethods", "admin.utils", "admin.shippingMethods", "admin.users", "textAngular", "admin.side_menu", "admin.taxons", 'admin.indexUtils', 'admin.tagRules', 'admin.dropdown', 'pasvaz.bindonce', 'ngSanitize'] ) \ No newline at end of file diff --git a/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee b/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee similarity index 82% rename from app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee rename to app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee index 096cce250d..7d8305118f 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/tag_rules_controller.js.coffee +++ b/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee @@ -1,5 +1,5 @@ -angular.module("admin.enterprises").controller "TagRulesCtrl", ($scope, $http) -> - $scope.tagGroups = $scope.Enterprise.tag_groups +angular.module("admin.tagRules").controller "TagRulesCtrl", ($scope, $http, enterprise) -> + $scope.tagGroups = enterprise.tag_groups updateRuleCounts = -> index = 0 @@ -35,7 +35,7 @@ angular.module("admin.enterprises").controller "TagRulesCtrl", ($scope, $http) - if confirm("Are you sure?") $http method: "DELETE" - url: "/admin/enterprises/#{$scope.Enterprise.id}/tag_rules/#{tagRule.id}.json" + url: "/admin/enterprises/#{enterprise.id}/tag_rules/#{tagRule.id}.json" .success -> tagGroup.rules.splice(index, 1) updateRuleCounts() diff --git a/app/assets/javascripts/admin/enterprises/directives/invert_number.js.coffee b/app/assets/javascripts/admin/tag_rules/directives/invert_number.js.coffee similarity index 83% rename from app/assets/javascripts/admin/enterprises/directives/invert_number.js.coffee rename to app/assets/javascripts/admin/tag_rules/directives/invert_number.js.coffee index 0557d3ed22..2412eec18c 100644 --- a/app/assets/javascripts/admin/enterprises/directives/invert_number.js.coffee +++ b/app/assets/javascripts/admin/tag_rules/directives/invert_number.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.enterprises").directive "invertNumber", -> +angular.module("admin.tagRules").directive "invertNumber", -> restrict: "A" require: "ngModel" link: (scope, element, attrs, ngModel) -> diff --git a/app/assets/javascripts/admin/enterprises/directives/tag_rules/discount_order.js.coffee b/app/assets/javascripts/admin/tag_rules/directives/tag_rules/discount_order.js.coffee similarity index 56% rename from app/assets/javascripts/admin/enterprises/directives/tag_rules/discount_order.js.coffee rename to app/assets/javascripts/admin/tag_rules/directives/tag_rules/discount_order.js.coffee index 079dd04dba..b374f88782 100644 --- a/app/assets/javascripts/admin/enterprises/directives/tag_rules/discount_order.js.coffee +++ b/app/assets/javascripts/admin/tag_rules/directives/tag_rules/discount_order.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.enterprises").directive "discountOrder", -> +angular.module("admin.tagRules").directive "discountOrder", -> restrict: "E" replace: true templateUrl: "admin/tag_rules/discount_order.html" diff --git a/app/assets/javascripts/admin/tag_rules/tag_rules.js.coffee b/app/assets/javascripts/admin/tag_rules/tag_rules.js.coffee new file mode 100644 index 0000000000..88c7734c33 --- /dev/null +++ b/app/assets/javascripts/admin/tag_rules/tag_rules.js.coffee @@ -0,0 +1 @@ +angular.module("admin.tagRules", ['ngTagsInput']) diff --git a/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee b/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee similarity index 95% rename from spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee rename to spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee index 988040694c..0b92822a0a 100644 --- a/spec/javascripts/unit/admin/enterprises/controllers/tag_rules_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee @@ -4,7 +4,7 @@ describe "TagRulesCtrl", -> enterprise = null beforeEach -> - module('admin.enterprises') + module('admin.tagRules') enterprise = id: 45 tag_groups: [ @@ -14,8 +14,7 @@ describe "TagRulesCtrl", -> inject ($rootScope, $controller) -> scope = $rootScope - scope.Enterprise = enterprise - ctrl = $controller 'TagRulesCtrl', {$scope: scope} + ctrl = $controller 'TagRulesCtrl', {$scope: scope, enterprise: enterprise} describe "tagGroup start indices", -> it "updates on initialization", -> From 33aad10e73c5c2925cdfd24a208f7f9948edc3c8 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 10:32:22 +1100 Subject: [PATCH 172/215] Adding dialogOptions library for adding responsiveness to jquery dialog --- app/assets/javascripts/admin/all.js | 1 + .../shared/jquery.dialogOptions.js | 191 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 app/assets/javascripts/shared/jquery.dialogOptions.js diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 9f99dc1dcd..6f592ae152 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -9,6 +9,7 @@ //= require jquery_ujs //= require jquery-ui //= require shared/jquery-ui-timepicker-addon +//= require shared/jquery.dialogOptions //= require angular //= require angular-resource //= require angular-animate diff --git a/app/assets/javascripts/shared/jquery.dialogOptions.js b/app/assets/javascripts/shared/jquery.dialogOptions.js new file mode 100644 index 0000000000..02f3bdc58a --- /dev/null +++ b/app/assets/javascripts/shared/jquery.dialogOptions.js @@ -0,0 +1,191 @@ +/* + * jQuery UI dialogOptions v1.0 + * @desc extending jQuery Ui Dialog - Responsive, click outside, class handling + * @author Jason Day + * + * Dependencies: + * jQuery: http://jquery.com/ + * jQuery UI: http://jqueryui.com/ + * Modernizr: http://modernizr.com/ + * + * MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * (c) Jason Day 2014 + * + * New Options: + * clickOut: true // closes dialog when clicked outside + * responsive: true // fluid width & height based on viewport + * // true: always responsive + * // false: never responsive + * // "touch": only responsive on touch device + * scaleH: 0.8 // responsive scale height percentage, 0.8 = 80% of viewport + * scaleW: 0.8 // responsive scale width percentage, 0.8 = 80% of viewport + * showTitleBar: true // false: hide titlebar + * showCloseButton: true // false: hide close button + * + * Added functionality: + * add & remove dialogClass to .ui-widget-overlay for scoping styles + * patch for: http://bugs.jqueryui.com/ticket/4671 + * recenter dialog - ajax loaded content + */ + +// add new options with default values +$.ui.dialog.prototype.options.clickOut = true; +$.ui.dialog.prototype.options.responsive = true; +$.ui.dialog.prototype.options.scaleH = 0.8; +$.ui.dialog.prototype.options.scaleW = 0.8; +$.ui.dialog.prototype.options.showTitleBar = true; +$.ui.dialog.prototype.options.showCloseButton = true; + + +// extend _init +var _init = $.ui.dialog.prototype._init; +$.ui.dialog.prototype._init = function () { + var self = this; + + // apply original arguments + _init.apply(this, arguments); + + //patch + if ($.ui && $.ui.dialog && $.ui.dialog.overlay) { + $.ui.dialog.overlay.events = $.map('focus,keydown,keypress'.split(','), function (event) { + return event + '.dialog-overlay'; + }).join(' '); + } +}; +// end _init + + +// extend open function +var _open = $.ui.dialog.prototype.open; +$.ui.dialog.prototype.open = function () { + var self = this; + + // apply original arguments + _open.apply(this, arguments); + + // get dialog original size on open + var oHeight = self.element.parent().outerHeight(), + oWidth = self.element.parent().outerWidth(), + isTouch = $("html").hasClass("touch"); + + // responsive width & height + var resize = function () { + + // check if responsive + // dependent on modernizr for device detection / html.touch + if (self.options.responsive === true || (self.options.responsive === "touch" && isTouch)) { + var elem = self.element, + wHeight = $(window).height(), + wWidth = $(window).width(), + dHeight = elem.parent().outerHeight(), + dWidth = elem.parent().outerWidth(), + setHeight = Math.min(wHeight * self.options.scaleH, oHeight), + setWidth = Math.min(wWidth * self.options.scaleW, oWidth); + + // check & set height + if ((oHeight + 100) > wHeight || elem.hasClass("resizedH")) { + elem.dialog("option", "height", setHeight).parent().css("max-height", setHeight); + elem.addClass("resizedH"); + } + + // check & set width + if ((oWidth + 100) > wWidth || elem.hasClass("resizedW")) { + elem.dialog("option", "width", setWidth).parent().css("max-width", setWidth); + elem.addClass("resizedW"); + } + + // only recenter & add overflow if dialog has been resized + if (elem.hasClass("resizedH") || elem.hasClass("resizedW")) { + elem.dialog("option", "position", "center"); + elem.css("overflow", "auto"); + } + } + + // add webkit scrolling to all dialogs for touch devices + if (isTouch) { + elem.css("-webkit-overflow-scrolling", "touch"); + } + }; + + // call resize() + resize(); + + // resize on window resize + $(window).on("resize", function () { + resize(); + }); + + // resize on orientation change + if (window.addEventListener) { // Add extra condition because IE8 doesn't support addEventListener (or orientationchange) + window.addEventListener("orientationchange", function () { + resize(); + }); + } + + // hide titlebar + if (!self.options.showTitleBar) { + self.uiDialogTitlebar.css({ + "height": 0, + "padding": 0, + "background": "none", + "border": 0 + }); + self.uiDialogTitlebar.find(".ui-dialog-title").css("display", "none"); + } + + //hide close button + if (!self.options.showCloseButton) { + self.uiDialogTitlebar.find(".ui-dialog-titlebar-close").css("display", "none"); + } + + // close on clickOut + if (self.options.clickOut && !self.options.modal) { + // use transparent div - simplest approach (rework) + $('
    ').insertBefore(self.element.parent()); + $('#dialog-overlay').css({ + "position": "fixed", + "top": 0, + "right": 0, + "bottom": 0, + "left": 0, + "background-color": "transparent" + }); + $('#dialog-overlay').click(function (e) { + e.preventDefault(); + e.stopPropagation(); + self.close(); + }); + // else close on modal click + } else if (self.options.clickOut && self.options.modal) { + $('.ui-widget-overlay').click(function (e) { + self.close(); + }); + } + + // add dialogClass to overlay + if (self.options.dialogClass) { + $('.ui-widget-overlay').addClass(self.options.dialogClass); + } +}; +//end open + + +// extend close function +var _close = $.ui.dialog.prototype.close; +$.ui.dialog.prototype.close = function () { + var self = this; + // apply original arguments + _close.apply(this, arguments); + + // remove dialogClass to overlay + if (self.options.dialogClass) { + $('.ui-widget-overlay').removeClass(self.options.dialogClass); + } + //remove clickOut overlay + if ($("#dialog-overlay").length) { + $("#dialog-overlay").remove(); + } +}; +//end close From ed134bac84f9e0e533b50c8b13d1d38c93fea170 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 10:42:54 +1100 Subject: [PATCH 173/215] Removing unrequired initSelection option from ofn-select2 --- .../admin/index_utils/directives/ofn-select2.js.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/admin/index_utils/directives/ofn-select2.js.coffee b/app/assets/javascripts/admin/index_utils/directives/ofn-select2.js.coffee index ec454e9216..132480d987 100644 --- a/app/assets/javascripts/admin/index_utils/directives/ofn-select2.js.coffee +++ b/app/assets/javascripts/admin/index_utils/directives/ofn-select2.js.coffee @@ -15,8 +15,6 @@ angular.module("admin.indexUtils").directive "ofnSelect2", ($sanitize, $timeout) element.select2 minimumResultsForSearch: scope.minSearch || 0 data: { results: scope.data, text: scope.text } - initSelection: (element, callback) -> - callback scope.data[0] formatSelection: (item) -> item[scope.text] formatResult: (item) -> From f70b1f457256627f27747a0b12be227dc1b79500 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 11:17:28 +1100 Subject: [PATCH 174/215] Refactoring tag rule model and adding new rule type: FilterShippingMethod --- app/models/tag_rule.rb | 11 ++- app/models/tag_rule/discount_order.rb | 4 +- .../tag_rule/filter_shipping_methods.rb | 32 +++++++ spec/factories.rb | 4 + .../tag_rule/filter_shipping_methods_spec.rb | 83 ++++++++++++++++++ spec/models/tag_rule_spec.rb | 85 +++++++++++-------- 6 files changed, 180 insertions(+), 39 deletions(-) create mode 100644 app/models/tag_rule/filter_shipping_methods.rb create mode 100644 spec/models/tag_rule/filter_shipping_methods_spec.rb diff --git a/app/models/tag_rule.rb b/app/models/tag_rule.rb index 0237b74eb2..6e6405a589 100644 --- a/app/models/tag_rule.rb +++ b/app/models/tag_rule.rb @@ -15,14 +15,19 @@ class TagRule < ActiveRecord::Base end def apply - apply! if relevant? + if relevant? + if customer_tags_match? + apply! + else + apply_default! if respond_to?(:apply_default!,true) + end + end end private def relevant? - return false unless subject.class == subject_class - return false unless customer_tags_match? + return false unless subject_class_matches? if respond_to?(:additional_requirements_met?, true) return false unless additional_requirements_met? end diff --git a/app/models/tag_rule/discount_order.rb b/app/models/tag_rule/discount_order.rb index c1e440760a..9f293bfdaa 100644 --- a/app/models/tag_rule/discount_order.rb +++ b/app/models/tag_rule/discount_order.rb @@ -8,8 +8,8 @@ class TagRule::DiscountOrder < TagRule create_adjustment(I18n.t("tag_rules.discount_order.discount"), subject, subject) end - def subject_class - Spree::Order + def subject_class_matches? + subject.class == Spree::Order end def additional_requirements_met? diff --git a/app/models/tag_rule/filter_shipping_methods.rb b/app/models/tag_rule/filter_shipping_methods.rb new file mode 100644 index 0000000000..74438e560e --- /dev/null +++ b/app/models/tag_rule/filter_shipping_methods.rb @@ -0,0 +1,32 @@ +class TagRule::FilterShippingMethods < TagRule + preference :matched_shipping_methods_visibility, :string, default: "visible" + preference :shipping_method_tags, :string, default: "" + + attr_accessible :preferred_matched_shipping_methods_visibility, :preferred_shipping_method_tags + + private + + # Warning: this should only EVER be called via TagRule#apply + def apply! + unless preferred_matched_shipping_methods_visibility == "visible" + subject.reject!{ |sm| tags_match?(sm) } + end + end + + def apply_default! + if preferred_matched_shipping_methods_visibility == "visible" + subject.reject!{ |sm| tags_match?(sm) } + end + end + + def tags_match?(shipping_method) + shipping_method_tags = shipping_method.andand.tag_list || [] + preferred_tags = preferred_shipping_method_tags.split(",") + ( shipping_method_tags & preferred_tags ).any? + end + + def subject_class_matches? + subject.class == Array && + subject.all? { |i| i.class == Spree::ShippingMethod } + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 37611c9d42..11b4fed010 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -286,6 +286,10 @@ FactoryGirl.define do month { 1 + rand(12) } end + factory :filter_shipping_method_tag_rule, class: TagRule::FilterShippingMethods do + enterprise { FactoryGirl.create :distributor_enterprise } + end + factory :tag_rule, class: TagRule::DiscountOrder do enterprise { FactoryGirl.create :distributor_enterprise } before(:create) do |tr| diff --git a/spec/models/tag_rule/filter_shipping_methods_spec.rb b/spec/models/tag_rule/filter_shipping_methods_spec.rb new file mode 100644 index 0000000000..2c9da2a17b --- /dev/null +++ b/spec/models/tag_rule/filter_shipping_methods_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe TagRule::DiscountOrder, type: :model do + let!(:tag_rule) { create(:filter_shipping_method_tag_rule) } + + describe "determining whether tags match for a given shipping method" do + context "when the shipping method is nil" do + + it "returns false" do + expect(tag_rule.send(:tags_match?, nil)).to be false + end + end + + context "when the shipping method is not nil" do + let(:shipping_method) { create(:shipping_method, tag_list: ["member","local","volunteer"]) } + + context "when the rule has no preferred shipping method tags specified" do + before { allow(tag_rule).to receive(:preferred_shipping_method_tags) { "" } } + it { expect(tag_rule.send(:tags_match?, shipping_method)).to be false } + end + + context "when the rule has preferred customer tags specified that match ANY of the customer tags" do + before { allow(tag_rule).to receive(:preferred_shipping_method_tags) { "wholesale,some_tag,member" } } + it { expect(tag_rule.send(:tags_match?, shipping_method)).to be true } + end + + context "when the rule has preferred customer tags specified that match NONE of the customer tags" do + before { allow(tag_rule).to receive(:preferred_shipping_method_tags) { "wholesale,some_tag,some_other_tag" } } + it { expect(tag_rule.send(:tags_match?, shipping_method)).to be false } + end + end + end + + describe "applying the rule" do + # Assume that all validation is done by the TagRule base class + + let(:sm1) { create(:shipping_method, tag_list: ["tag1", "something", "somethingelse"]) } + let(:sm2) { create(:shipping_method, tag_list: ["tag2"]) } + let(:sm3) { create(:shipping_method, tag_list: ["tag3"]) } + let!(:shipping_methods) { [sm1, sm2, sm3] } + + before do + tag_rule.update_attribute(:preferred_shipping_method_tags, "tag2") + tag_rule.set_context(shipping_methods, nil) + end + + context "apply!" do + context "when showing matching shipping methods" do + before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "visible") } + it "does nothing" do + tag_rule.send(:apply!) + expect(shipping_methods).to eq [sm1, sm2, sm3] + end + end + + context "when hiding matching shipping methods" do + before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "hidden") } + it "removes matching shipping methods from the list" do + tag_rule.send(:apply!) + expect(shipping_methods).to eq [sm1, sm3] + end + end + end + + context "apply_default!" do + context "when showing matching shipping methods" do + before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "visible") } + it "remove matching shipping methods from the list" do + tag_rule.send(:apply_default!) + expect(shipping_methods).to eq [sm1, sm3] + end + end + + context "when hiding matching shipping methods" do + before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "hidden") } + it "does nothing" do + tag_rule.send(:apply_default!) + expect(shipping_methods).to eq [sm1, sm2, sm3] + end + end + end + end +end diff --git a/spec/models/tag_rule_spec.rb b/spec/models/tag_rule_spec.rb index c5107a3dfe..549c2f88aa 100644 --- a/spec/models/tag_rule_spec.rb +++ b/spec/models/tag_rule_spec.rb @@ -43,50 +43,38 @@ describe TagRule, type: :model do allow(subject).to receive(:class) { Spree::Order } end - context "when customer_tags_match? returns false" do - before { expect(tag_rule).to receive(:customer_tags_match?) { false } } + context "when the rule does not repond to #additional_requirements_met?" do + before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { false } } - it "returns the value of customer_tags_match?" do - expect(tag_rule.send(:relevant?)).to be false + it "returns true" do + expect(tag_rule.send(:relevant?)).to be true end end - context "when customer_tags_match? returns true" do - before { expect(tag_rule).to receive(:customer_tags_match?) { true } } + context "when the rule reponds to #additional_requirements_met?" do + before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { true } } - context "when the rule does not repond to #additional_requirements_met?" do - before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { false } } + context "and #additional_requirements_met? returns a truthy value" do + before { allow(tag_rule).to receive(:additional_requirements_met?) { "smeg" } } - it "returns true" do + it "returns true immediately" do expect(tag_rule.send(:relevant?)).to be true end end - context "when the rule reponds to #additional_requirements_met?" do - before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { true } } + context "and #additional_requirements_met? returns true" do + before { allow(tag_rule).to receive(:additional_requirements_met?) { true } } - context "and #additional_requirements_met? returns a truthy value" do - before { allow(tag_rule).to receive(:additional_requirements_met?) { "smeg" } } - - it "returns true immediately" do - expect(tag_rule.send(:relevant?)).to be true - end + it "returns true immediately" do + expect(tag_rule.send(:relevant?)).to be true end + end - context "and #additional_requirements_met? returns true" do - before { allow(tag_rule).to receive(:additional_requirements_met?) { true } } + context "and #additional_requirements_met? returns false" do + before { allow(tag_rule).to receive(:additional_requirements_met?) { false } } - it "returns true immediately" do - expect(tag_rule.send(:relevant?)).to be true - end - end - - context "and #additional_requirements_met? returns false" do - before { allow(tag_rule).to receive(:additional_requirements_met?) { false } } - - it "returns false immediately" do - expect(tag_rule.send(:relevant?)).to be false - end + it "returns false immediately" do + expect(tag_rule.send(:relevant?)).to be false end end end @@ -165,13 +153,42 @@ describe TagRule, type: :model do context "when the rule is deemed to be relevant" do before { allow(tag_rule).to receive(:relevant?) { true } } - it "applies the rule" do - tag_rule.apply - expect(tag_rule).to have_received(:apply!) + context "and customer_tags_match? returns true" do + before { expect(tag_rule).to receive(:customer_tags_match?) { true } } + + it "applies the rule" do + tag_rule.apply + expect(tag_rule).to have_received(:apply!) + end + end + + context "when customer_tags_match? returns false" do + before { expect(tag_rule).to receive(:customer_tags_match?) { false } } + before { allow(tag_rule).to receive(:apply_default!) } + + context "and the rule responds to #apply_default!" do + before { allow(tag_rule).to receive(:respond_to?).with(:apply_default!, true) { true } } + + it "applies the default action" do + tag_rule.apply + expect(tag_rule).to_not have_received(:apply!) + expect(tag_rule).to have_received(:apply_default!) + end + end + + context "and the rule does not respond to #apply_default!" do + before { allow(tag_rule).to receive(:respond_to?).with(:apply_default!, true) { false } } + + it "does not apply the rule or the default action" do + tag_rule.apply + expect(tag_rule).to_not have_received(:apply!) + expect(tag_rule).to_not have_received(:apply_default!) + end + end end end - context "when the rule is deemed to be relevant" do + context "when the rule is deemed not to be relevant" do before { allow(tag_rule).to receive(:relevant?) { false } } it "does not apply the rule" do From 47df106237cb9aa593d473ead7c199833d4ebfc2 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 11:20:32 +1100 Subject: [PATCH 175/215] Refactor tragsWithTranslation directive to handle dynamic attr names --- .../directives/tags_with_translation.js.coffee | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/admin/utils/directives/tags_with_translation.js.coffee b/app/assets/javascripts/admin/utils/directives/tags_with_translation.js.coffee index eae1a899c1..6ce7953608 100644 --- a/app/assets/javascripts/admin/utils/directives/tags_with_translation.js.coffee +++ b/app/assets/javascripts/admin/utils/directives/tags_with_translation.js.coffee @@ -1,8 +1,15 @@ -angular.module("admin.utils").directive "tagsWithTranslation", -> +angular.module("admin.utils").directive "tagsWithTranslation", ($timeout) -> restrict: "E" - template: "" + template: "" scope: object: "=" + tagsAttr: "@?" + tagListAttr: "@?" link: (scope, element, attrs) -> - scope.$watchCollection "object.tags", -> - scope.object.tag_list = (tag.text for tag in scope.object.tags).join(",") + $timeout -> + scope.tagsAttr ||= "tags" + scope.tagListAttr ||= "tag_list" + + watchString = "object.#{scope.tagsAttr}" + scope.$watchCollection watchString, -> + scope.object[scope.tagListAttr] = (tag.text for tag in scope.object[scope.tagsAttr]).join(",") From 82bc26fe9065c8eac83b7a94df85d1634ea6138a Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 11:33:36 +1100 Subject: [PATCH 176/215] Splitting tag rule feature specs out into their own spec file --- spec/features/admin/enterprises_spec.rb | 77 ------------------------ spec/features/admin/tag_rules_spec.rb | 80 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 77 deletions(-) create mode 100644 spec/features/admin/tag_rules_spec.rb diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index a39a3b8acb..3579421b84 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -282,83 +282,6 @@ feature %q{ end end - describe "tag rules", js: true do - let!(:enterprise) { create(:distributor_enterprise) } - - context "creating" do - before do - login_to_admin_section - visit main_app.edit_admin_enterprise_path(enterprise) - end - - it "creates a new rule" do - click_link "Tag Rules" - - expect(page).to_not have_selector '.customer_tag' - expect(page).to have_content 'No tags apply to this enterprise yet' - click_button '+ Add A New Tag' - find(:css, "tags-input .tags input").set "volunteer\n" - - expect(page).to have_content 'No rules apply to this tag yet' - click_button '+ Add A New Rule' - fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 22 - - click_button 'Update' - - tag_rule = TagRule::DiscountOrder.last - expect(tag_rule.preferred_customer_tags).to eq "volunteer" - expect(tag_rule.calculator.preferred_flat_percent).to eq -22 - end - end - - context "updating" do - let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } - - before do - login_to_admin_section - visit main_app.edit_admin_enterprise_path(enterprise) - end - - it "saves changes to the rule" do - click_link "Tag Rules" - - expect(first('.customer_tag .header')).to have_content "For customers tagged:" - expect(first('tags-input .tag-list ti-tag-item')).to have_content "member" - find(:css, "tags-input .tags input").set "volunteer\n" - expect(page).to have_field "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: '0' - fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 45 - - click_button 'Update' - - expect(tag_rule.preferred_customer_tags).to eq "member,volunteer" - expect(tag_rule.calculator.preferred_flat_percent).to eq -45 - end - end - - context "deleting" do - let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } - - before do - login_to_admin_section - visit main_app.edit_admin_enterprise_path(enterprise) - end - - it "deletes rules from the database" do - click_link "Tag Rules" - - expect(page).to have_selector "#tr_#{tag_rule.id}" - - expect{ - within "#tr_#{tag_rule.id}" do - first("a.delete-tag-rule").click - end - expect(page).to_not have_selector "#tr_#{tag_rule.id}" - }.to change{TagRule.count}.by(-1) - end - end - end - - context "as an Enterprise user", js: true do let(:supplier1) { create(:supplier_enterprise, name: 'First Supplier') } let(:supplier2) { create(:supplier_enterprise, name: 'Another Supplier') } diff --git a/spec/features/admin/tag_rules_spec.rb b/spec/features/admin/tag_rules_spec.rb new file mode 100644 index 0000000000..d905b2fef1 --- /dev/null +++ b/spec/features/admin/tag_rules_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +feature 'Tag Rules', js: true do + include AuthenticationWorkflow + include WebHelper + + let!(:enterprise) { create(:distributor_enterprise) } + + context "creating" do + before do + login_to_admin_section + visit main_app.edit_admin_enterprise_path(enterprise) + end + + it "creates a new rule" do + click_link "Tag Rules" + + expect(page).to_not have_selector '.customer_tag' + expect(page).to have_content 'No tags apply to this enterprise yet' + click_button '+ Add A New Tag' + find(:css, "tags-input .tags input").set "volunteer\n" + + expect(page).to have_content 'No rules apply to this tag yet' + click_button '+ Add A New Rule' + fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 22 + + click_button 'Update' + + tag_rule = TagRule::DiscountOrder.last + expect(tag_rule.preferred_customer_tags).to eq "volunteer" + expect(tag_rule.calculator.preferred_flat_percent).to eq -22 + end + end + + context "updating" do + let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } + + before do + login_to_admin_section + visit main_app.edit_admin_enterprise_path(enterprise) + end + + it "saves changes to the rule" do + click_link "Tag Rules" + + expect(first('.customer_tag .header')).to have_content "For customers tagged:" + expect(first('tags-input .tag-list ti-tag-item')).to have_content "member" + find(:css, "tags-input .tags input").set "volunteer\n" + expect(page).to have_field "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: '0' + fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 45 + + click_button 'Update' + + expect(tag_rule.preferred_customer_tags).to eq "member,volunteer" + expect(tag_rule.calculator.preferred_flat_percent).to eq -45 + end + end + + context "deleting" do + let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } + + before do + login_to_admin_section + visit main_app.edit_admin_enterprise_path(enterprise) + end + + it "deletes rules from the database" do + click_link "Tag Rules" + + expect(page).to have_selector "#tr_#{tag_rule.id}" + + expect{ + within "#tr_#{tag_rule.id}" do + first("a.delete-tag-rule").click + end + expect(page).to_not have_selector "#tr_#{tag_rule.id}" + }.to change{TagRule.count}.by(-1) + end + end +end From d278b72289a97326456015e758b78b9ddfd3434b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 12:22:54 +1100 Subject: [PATCH 177/215] Updating Tag Rules UI to allow management of new FilterShippingMethods rule type --- .../tag_rules_controller.js.coffee | 21 +++-- .../directives/new_rule_dialog.js.coffee | 31 +++++++ .../filter_shipping_methods.js.coffee | 4 + .../admin/new_tag_rule_dialog.html.haml | 10 +++ .../admin/tag_rules/discount_order.html.haml | 16 ++-- .../filter_shipping_methods.html.haml | 27 ++++++ .../admin/components/jquery_dialog.scss | 88 +++++++++++++++++++ app/assets/stylesheets/admin/select2.css.scss | 35 ++++++++ .../stylesheets/admin/tag_rules.css.scss | 9 +- .../api/admin/tag_rule_serializer.rb | 8 ++ .../enterprises/form/_tag_rules.html.haml | 3 +- spec/factories.rb | 2 +- spec/features/admin/tag_rules_spec.rb | 39 ++++++-- .../tag_rules_controller_spec.js.coffee | 5 +- .../tag_rule/filter_shipping_methods_spec.rb | 2 +- 15 files changed, 273 insertions(+), 27 deletions(-) create mode 100644 app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee create mode 100644 app/assets/javascripts/admin/tag_rules/directives/tag_rules/filter_shipping_methods.js.coffee create mode 100644 app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml create mode 100644 app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods.html.haml create mode 100644 app/assets/stylesheets/admin/components/jquery_dialog.scss diff --git a/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee b/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee index 7d8305118f..7209147b66 100644 --- a/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee +++ b/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee @@ -1,6 +1,8 @@ angular.module("admin.tagRules").controller "TagRulesCtrl", ($scope, $http, enterprise) -> $scope.tagGroups = enterprise.tag_groups + $scope.visibilityOptions = [ { id: "visible", name: "VISIBLE" }, { id: "hidden", name: "NOT VISIBLE" } ] + updateRuleCounts = -> index = 0 for tagGroup in $scope.tagGroups @@ -13,13 +15,18 @@ angular.module("admin.tagRules").controller "TagRulesCtrl", ($scope, $http, ente for tagRule in tagGroup.rules tagRule.preferred_customer_tags = (tag.text for tag in tagGroup.tags).join(",") - $scope.addNewRuleTo = (tagGroup) -> - tagGroup.rules.push - id: null - preferred_customer_tags: (tag.text for tag in tagGroup.tags).join(",") - type: "TagRule::DiscountOrder" - calculator: - preferred_flat_percent: 0 + $scope.addNewRuleTo = (tagGroup, ruleType) -> + newRule = + id: null + preferred_customer_tags: (tag.text for tag in tagGroup.tags).join(",") + type: "TagRule::#{ruleType}" + switch ruleType + when "DiscountOrder" + newRule.calculator = { preferred_flat_percent: 0 } + when "FilterShippingMethods" + newRule.peferred_shipping_method_tags = [] + newRule.preferred_matched_shipping_methods_visibility = "visible" + tagGroup.rules.push(newRule) updateRuleCounts() $scope.addNewTag = -> diff --git a/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee b/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee new file mode 100644 index 0000000000..981699a96d --- /dev/null +++ b/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee @@ -0,0 +1,31 @@ +angular.module("admin.tagRules").directive 'newTagRuleDialog', ($compile, $templateCache) -> + restrict: 'A' + scope: true + link: (scope, element, attr) -> + # Compile modal template + template = $compile($templateCache.get('admin/new_tag_rule_dialog.html'))(scope) + + scope.ruleTypes = [ + { id: "DiscountOrder", name: 'Apply a discount to orders' } + { id: "FilterShippingMethods", name: 'Show/Hide shipping methods' } + ] + + scope.ruleType = "DiscountOrder" + + # Set Dialog options + template.dialog + autoOpen: false + resizable: false + width: 'auto' + scaleW: 0.4 + modal: true + clickOut: true + + # Link opening of dialog to click event on element + element.bind 'click', (e) -> + template.dialog('open') + + scope.addRule = (tagGroup, ruleType) -> + scope.addNewRuleTo(tagGroup, ruleType) + template.dialog('close') + return diff --git a/app/assets/javascripts/admin/tag_rules/directives/tag_rules/filter_shipping_methods.js.coffee b/app/assets/javascripts/admin/tag_rules/directives/tag_rules/filter_shipping_methods.js.coffee new file mode 100644 index 0000000000..1a75cf8ff2 --- /dev/null +++ b/app/assets/javascripts/admin/tag_rules/directives/tag_rules/filter_shipping_methods.js.coffee @@ -0,0 +1,4 @@ +angular.module("admin.tagRules").directive "filterShippingMethods", -> + restrict: "E" + replace: true + templateUrl: "admin/tag_rules/filter_shipping_methods.html" diff --git a/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml b/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml new file mode 100644 index 0000000000..653e0d175d --- /dev/null +++ b/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml @@ -0,0 +1,10 @@ +#new-tag-rule-dialog + .text-normal.margin-bottom-30.text-center + Select a rule type: + + .text-center.margin-bottom-30 + -# %select.fullwidth{ 'select2-min-search' => 5, 'ng-model' => 'newRuleType', 'ng-options' => 'ruleType.id as ruleType.name for ruleType in availableRuleTypes' } + %input.ofn-select2.fullwidth{ :id => 'rule_type_selector', ng: { model: "ruleType" }, data: "ruleTypes", 'min-search' => "5" } + + .text-center + %input.button.red.icon-plus{ type: 'button', value: "Add Rule", ng: { click: 'addRule(tagGroup, ruleType)' } } diff --git a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml index 90886b2dab..358d9ce1a6 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml @@ -28,12 +28,10 @@ name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][calculator_attributes][preferred_flat_percent]", ng: { value: "rule.calculator.preferred_flat_percent" } } - %span.text-normal {{ $index + 1 }}. Apply a discount of - %span.input-symbol.after - %span.text-normal % - %input.text-big{ type: "number", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", - min: -100, - max: 100, - ng: { model: "rule.calculator.preferred_flat_percent" }, 'invert-number' => true } - %span.text-normal to order subtotals + %span.text-normal {{ $index + 1 }}. Orders are discounted by + %input{ type: "number", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", + min: -100, + max: 100, + ng: { model: "rule.calculator.preferred_flat_percent" }, 'invert-number' => true } + %span.text-normal % diff --git a/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods.html.haml b/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods.html.haml new file mode 100644 index 0000000000..6552d834b5 --- /dev/null +++ b/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods.html.haml @@ -0,0 +1,27 @@ +%div + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_id", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][id]", + ng: { value: "rule.id" } } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_type", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][type]", + value: "TagRule::FilterShippingMethods" } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_customer_tags", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_customer_tags]", + ng: { value: "rule.preferred_customer_tags" } } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_shipping_method_tags", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_shipping_method_tags]", + ng: { value: "rule.preferred_customer_tags" } } + + %span.text-normal {{ $index + 1 }}. Shipping methods with matching tags are + %input.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", + ng: { model: "rule.preferred_matched_shipping_methods_visibility"}, + data: 'visibilityOptions', "min-search" => 5 } + -# %tags-with-translation{ object: "rule", "tags-attr" => "shipping_method_tags", "tag-list-attr" => "preferred_shipping_method_tags" } diff --git a/app/assets/stylesheets/admin/components/jquery_dialog.scss b/app/assets/stylesheets/admin/components/jquery_dialog.scss new file mode 100644 index 0000000000..2e36db6e33 --- /dev/null +++ b/app/assets/stylesheets/admin/components/jquery_dialog.scss @@ -0,0 +1,88 @@ +/** +Main colors: +dark: #545454 +light: #ccc +*/ +.ui-dialog { + border: 2px solid #4a4a4a; + border-radius:3px; + padding:0px; + -moz-box-shadow: 3px 3px 4px #797979; + -webkit-box-shadow: 3px 3px 4px #797979; + box-shadow: 3px 3px 4px #797979; + + /* For IE 8 */ + -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#545454')"; + + /* For IE 5.5 - 7 */ + filter: progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#545454'); +} + +.ui-dialog .ui-dialog-titlebar{ + border-radius: 3px; +} + +.ui-dialog .ui-state-hover { + &.ui-dialog-titlebar-close{ + + } +} + +/*.ui-dialog .ui-icon-closethick{background:url(/static/assets/dialogCloseButton.png);}*/ + +.ui-dialog .ui-widget-header{ + background-image: none; + background-color: #ffffff; + border:0px; + border-radius: 3px; + padding: 0px 5px 0px 5px; +} +.ui-dialog .ui-widget-content{ + border: none; + border-radius: 3px; + padding: 0px 50px 30px 50px; +} + +.ui-dialog .ui-corner-all{ + border-radius:0px; +} +.ui-dialog { + .ui-state-hover, .ui-state-focus{ + border: none; + background: none; + color: #545454; + } +} + +.ui-state-hover, .ui-widget-header .ui-state-hover, .ui-widget-content .ui-state-hover { + background-color: #ffffff; + background: none; +} + +.ui-dialog-titlebar-close { + float: right; + &:before { + color: #000000; + font-size: 2em; + font-weight: 400; + content: '\00d7'; + display: inline; + } + + &:hover { + &:before { + color: #da5354; + } + } + + .ui-icon { + &.ui-icon-closethick { + display: none; + } + } +} + +.ui-widget-overlay { + background: #e9e9e9; + opacity: 0.6; +} diff --git a/app/assets/stylesheets/admin/select2.css.scss b/app/assets/stylesheets/admin/select2.css.scss index daba11d099..f94515a54c 100644 --- a/app/assets/stylesheets/admin/select2.css.scss +++ b/app/assets/stylesheets/admin/select2.css.scss @@ -1,5 +1,8 @@ .select2-container { .select2-choice { + .select2-search-choice-close { + display: none; + } .select2-arrow { width: 22px; border: none; @@ -7,4 +10,36 @@ background-color: transparent; } } + + &.light { + .select2-choice{ + background-color: #ffffff; + font-weight: normal; + border: 1px solid #5498da !important; + color: #5498da !important; + + .select2-arrow { + &:before { + color: #5498da; + font-size: 1rem; + font-weight: 400; + content: '\25be'; + display: inline; + } + } + } + + &:hover, &.select2-container-active { + .select2-choice{ + color: #ffffff !important; + background-color: #5498da !important; + + .select2-arrow { + &:before { + color: #ffffff; + } + } + } + } + } } diff --git a/app/assets/stylesheets/admin/tag_rules.css.scss b/app/assets/stylesheets/admin/tag_rules.css.scss index 985ed7d727..028ad6ce1c 100644 --- a/app/assets/stylesheets/admin/tag_rules.css.scss +++ b/app/assets/stylesheets/admin/tag_rules.css.scss @@ -44,7 +44,7 @@ td { border: none; - padding: 4px 10px; + padding: 4px 10px 10px 10px; margin: 0px; input { @@ -59,3 +59,10 @@ margin-bottom: 10px; } } + +#new-tag-rule-dialog{ + .select2-chosen, .select2-result-label{ + font-size: 1rem; + font-weight: lighter; + } +} diff --git a/app/serializers/api/admin/tag_rule_serializer.rb b/app/serializers/api/admin/tag_rule_serializer.rb index a571b9fe5e..ac333c23c6 100644 --- a/app/serializers/api/admin/tag_rule_serializer.rb +++ b/app/serializers/api/admin/tag_rule_serializer.rb @@ -16,4 +16,12 @@ module Api::Admin::TagRule class DiscountOrderSerializer < BaseSerializer has_one :calculator, serializer: Api::Admin::Calculator::FlatPercentItemTotalSerializer end + + class FilterShippingMethodsSerializer < BaseSerializer + attributes :preferred_matched_shipping_methods_visibility, :shipping_method_tags + + def shipping_method_tags + object.preferred_shipping_method_tags.split(",") + end + end end diff --git a/app/views/admin/enterprises/form/_tag_rules.html.haml b/app/views/admin/enterprises/form/_tag_rules.html.haml index 9a3c0f7a40..1a50e4f353 100644 --- a/app/views/admin/enterprises/form/_tag_rules.html.haml +++ b/app/views/admin/enterprises/form/_tag_rules.html.haml @@ -24,9 +24,10 @@ %tr.tag_rule{ id: "tr_{{rule.id}}", ng: { repeat: "rule in tagGroup.rules" } } %td %discount-order{ bo: { if: "rule.type == 'TagRule::DiscountOrder'" } } + %filter-shipping-methods{ bo: { if: "rule.type == 'TagRule::FilterShippingMethods'" } } %td.actions %a{ ng: { click: "deleteTagRule(tagGroup, rule)" }, :class => "delete-tag-rule icon-trash no-text" } .add_rule.text-center - %input.button.icon-plus{ type: 'button', value: "+ Add A New Rule", ng: { click: 'addNewRuleTo(tagGroup)' } } + %input.button.icon-plus{ type: 'button', value: "+ Add A New Rule", "new-tag-rule-dialog" => true } .add_tage %input.button.red.icon-plus{ type: 'button', value: "+ Add A New Tag", ng: { click: 'addNewTag()' } } diff --git a/spec/factories.rb b/spec/factories.rb index 11b4fed010..b4a3a1b573 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -286,7 +286,7 @@ FactoryGirl.define do month { 1 + rand(12) } end - factory :filter_shipping_method_tag_rule, class: TagRule::FilterShippingMethods do + factory :filter_shipping_methods_tag_rule, class: TagRule::FilterShippingMethods do enterprise { FactoryGirl.create :distributor_enterprise } end diff --git a/spec/features/admin/tag_rules_spec.rb b/spec/features/admin/tag_rules_spec.rb index d905b2fef1..a4e6e62d24 100644 --- a/spec/features/admin/tag_rules_spec.rb +++ b/spec/features/admin/tag_rules_spec.rb @@ -12,47 +12,76 @@ feature 'Tag Rules', js: true do visit main_app.edit_admin_enterprise_path(enterprise) end - it "creates a new rule" do + it "allows creation of rules of each type" do click_link "Tag Rules" + # Creating a new tag expect(page).to_not have_selector '.customer_tag' expect(page).to have_content 'No tags apply to this enterprise yet' click_button '+ Add A New Tag' find(:css, "tags-input .tags input").set "volunteer\n" + # New DiscountOrder Rule expect(page).to have_content 'No rules apply to this tag yet' click_button '+ Add A New Rule' + select2_select 'Apply a discount to orders', from: 'rule_type_selector' + click_button "Add Rule" fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 22 + # New FilterShippingMethods Rule + click_button '+ Add A New Rule' + select2_select 'Show/Hide shipping methods', from: 'rule_type_selector' + click_button "Add Rule" + select2_select "NOT VISIBLE", from: "enterprise_tag_rules_attributes_1_preferred_matched_shipping_methods_visibility" + click_button 'Update' tag_rule = TagRule::DiscountOrder.last expect(tag_rule.preferred_customer_tags).to eq "volunteer" expect(tag_rule.calculator.preferred_flat_percent).to eq -22 + + tag_rule = TagRule::FilterShippingMethods.last + expect(tag_rule.preferred_customer_tags).to eq "volunteer" + expect(tag_rule.preferred_shipping_method_tags).to eq "volunteer" + expect(tag_rule.preferred_matched_shipping_methods_visibility).to eq "hidden" end end context "updating" do - let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } + let!(:do_tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } + let!(:fsm_tag_rule) { create(:filter_shipping_methods_tag_rule, enterprise: enterprise, preferred_matched_shipping_methods_visibility: "hidden", preferred_customer_tags: "member" ) } before do login_to_admin_section visit main_app.edit_admin_enterprise_path(enterprise) end - it "saves changes to the rule" do + it "saves changes to rules of each type" do click_link "Tag Rules" + # Tag group exists expect(first('.customer_tag .header')).to have_content "For customers tagged:" expect(first('tags-input .tag-list ti-tag-item')).to have_content "member" find(:css, "tags-input .tags input").set "volunteer\n" + + # DiscountOrder rule expect(page).to have_field "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: '0' fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 45 + # FilterShippingMethods rule + expect(page).to have_select2 "enterprise_tag_rules_attributes_1_preferred_matched_shipping_methods_visibility", selected: 'NOT VISIBLE' + select2_select 'VISIBLE', from: "enterprise_tag_rules_attributes_1_preferred_matched_shipping_methods_visibility" + click_button 'Update' - expect(tag_rule.preferred_customer_tags).to eq "member,volunteer" - expect(tag_rule.calculator.preferred_flat_percent).to eq -45 + # DiscountOrder rule + expect(do_tag_rule.preferred_customer_tags).to eq "member,volunteer" + expect(do_tag_rule.calculator.preferred_flat_percent).to eq -45 + + # FilterShippingMethods rule + expect(fsm_tag_rule.preferred_customer_tags).to eq "member,volunteer" + expect(fsm_tag_rule.preferred_shipping_method_tags).to eq "member,volunteer" + expect(fsm_tag_rule.preferred_matched_shipping_methods_visibility).to eq "visible" end end diff --git a/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee b/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee index 0b92822a0a..1e132ec07d 100644 --- a/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee @@ -23,10 +23,11 @@ describe "TagRulesCtrl", -> describe "adding a new tag group", -> beforeEach -> - scope.addNewRuleTo(scope.tagGroups[0]) + scope.addNewRuleTo(scope.tagGroups[0], "DiscountOrder") - it "adds a new rule to the rules array for the tagGroup", -> + it "adds a new rule of the specified type to the rules array for the tagGroup", -> expect(scope.tagGroups[0].rules.length).toEqual 3 + expect(scope.tagGroups[0].rules[2].type).toEqual "TagRule::DiscountOrder" it "updates tagGroup start indices", -> expect(scope.tagGroups[0].startIndex).toEqual 0 diff --git a/spec/models/tag_rule/filter_shipping_methods_spec.rb b/spec/models/tag_rule/filter_shipping_methods_spec.rb index 2c9da2a17b..539aa3c6ca 100644 --- a/spec/models/tag_rule/filter_shipping_methods_spec.rb +++ b/spec/models/tag_rule/filter_shipping_methods_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe TagRule::DiscountOrder, type: :model do - let!(:tag_rule) { create(:filter_shipping_method_tag_rule) } + let!(:tag_rule) { create(:filter_shipping_methods_tag_rule) } describe "determining whether tags match for a given shipping method" do context "when the shipping method is nil" do From c099711f97e2b10488a98e96c7cd6e228a61abfa Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 13:35:29 +1100 Subject: [PATCH 178/215] Removing dialogOptions library, not required anymore --- app/assets/javascripts/admin/all.js | 1 - .../directives/new_rule_dialog.js.coffee | 11 +- .../shared/jquery.dialogOptions.js | 191 ------------------ 3 files changed, 7 insertions(+), 196 deletions(-) delete mode 100644 app/assets/javascripts/shared/jquery.dialogOptions.js diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 6f592ae152..9f99dc1dcd 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -9,7 +9,6 @@ //= require jquery_ujs //= require jquery-ui //= require shared/jquery-ui-timepicker-addon -//= require shared/jquery.dialogOptions //= require angular //= require angular-resource //= require angular-animate diff --git a/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee b/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee index 981699a96d..888ec2b402 100644 --- a/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee +++ b/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.tagRules").directive 'newTagRuleDialog', ($compile, $templateCache) -> +angular.module("admin.tagRules").directive 'newTagRuleDialog', ($compile, $templateCache, $window) -> restrict: 'A' scope: true link: (scope, element, attr) -> @@ -14,12 +14,15 @@ angular.module("admin.tagRules").directive 'newTagRuleDialog', ($compile, $templ # Set Dialog options template.dialog + show: { effect: "fade", duration: 400 } + hide: { effect: "fade", duration: 300 } autoOpen: false resizable: false - width: 'auto' - scaleW: 0.4 + width: $window.innerWidth * 0.4; modal: true - clickOut: true + open: (event, ui) -> + $('.ui-widget-overlay').bind 'click', -> + $(this).siblings('.ui-dialog').find('.ui-dialog-content').dialog('close') # Link opening of dialog to click event on element element.bind 'click', (e) -> diff --git a/app/assets/javascripts/shared/jquery.dialogOptions.js b/app/assets/javascripts/shared/jquery.dialogOptions.js deleted file mode 100644 index 02f3bdc58a..0000000000 --- a/app/assets/javascripts/shared/jquery.dialogOptions.js +++ /dev/null @@ -1,191 +0,0 @@ -/* - * jQuery UI dialogOptions v1.0 - * @desc extending jQuery Ui Dialog - Responsive, click outside, class handling - * @author Jason Day - * - * Dependencies: - * jQuery: http://jquery.com/ - * jQuery UI: http://jqueryui.com/ - * Modernizr: http://modernizr.com/ - * - * MIT license: - * http://www.opensource.org/licenses/mit-license.php - * - * (c) Jason Day 2014 - * - * New Options: - * clickOut: true // closes dialog when clicked outside - * responsive: true // fluid width & height based on viewport - * // true: always responsive - * // false: never responsive - * // "touch": only responsive on touch device - * scaleH: 0.8 // responsive scale height percentage, 0.8 = 80% of viewport - * scaleW: 0.8 // responsive scale width percentage, 0.8 = 80% of viewport - * showTitleBar: true // false: hide titlebar - * showCloseButton: true // false: hide close button - * - * Added functionality: - * add & remove dialogClass to .ui-widget-overlay for scoping styles - * patch for: http://bugs.jqueryui.com/ticket/4671 - * recenter dialog - ajax loaded content - */ - -// add new options with default values -$.ui.dialog.prototype.options.clickOut = true; -$.ui.dialog.prototype.options.responsive = true; -$.ui.dialog.prototype.options.scaleH = 0.8; -$.ui.dialog.prototype.options.scaleW = 0.8; -$.ui.dialog.prototype.options.showTitleBar = true; -$.ui.dialog.prototype.options.showCloseButton = true; - - -// extend _init -var _init = $.ui.dialog.prototype._init; -$.ui.dialog.prototype._init = function () { - var self = this; - - // apply original arguments - _init.apply(this, arguments); - - //patch - if ($.ui && $.ui.dialog && $.ui.dialog.overlay) { - $.ui.dialog.overlay.events = $.map('focus,keydown,keypress'.split(','), function (event) { - return event + '.dialog-overlay'; - }).join(' '); - } -}; -// end _init - - -// extend open function -var _open = $.ui.dialog.prototype.open; -$.ui.dialog.prototype.open = function () { - var self = this; - - // apply original arguments - _open.apply(this, arguments); - - // get dialog original size on open - var oHeight = self.element.parent().outerHeight(), - oWidth = self.element.parent().outerWidth(), - isTouch = $("html").hasClass("touch"); - - // responsive width & height - var resize = function () { - - // check if responsive - // dependent on modernizr for device detection / html.touch - if (self.options.responsive === true || (self.options.responsive === "touch" && isTouch)) { - var elem = self.element, - wHeight = $(window).height(), - wWidth = $(window).width(), - dHeight = elem.parent().outerHeight(), - dWidth = elem.parent().outerWidth(), - setHeight = Math.min(wHeight * self.options.scaleH, oHeight), - setWidth = Math.min(wWidth * self.options.scaleW, oWidth); - - // check & set height - if ((oHeight + 100) > wHeight || elem.hasClass("resizedH")) { - elem.dialog("option", "height", setHeight).parent().css("max-height", setHeight); - elem.addClass("resizedH"); - } - - // check & set width - if ((oWidth + 100) > wWidth || elem.hasClass("resizedW")) { - elem.dialog("option", "width", setWidth).parent().css("max-width", setWidth); - elem.addClass("resizedW"); - } - - // only recenter & add overflow if dialog has been resized - if (elem.hasClass("resizedH") || elem.hasClass("resizedW")) { - elem.dialog("option", "position", "center"); - elem.css("overflow", "auto"); - } - } - - // add webkit scrolling to all dialogs for touch devices - if (isTouch) { - elem.css("-webkit-overflow-scrolling", "touch"); - } - }; - - // call resize() - resize(); - - // resize on window resize - $(window).on("resize", function () { - resize(); - }); - - // resize on orientation change - if (window.addEventListener) { // Add extra condition because IE8 doesn't support addEventListener (or orientationchange) - window.addEventListener("orientationchange", function () { - resize(); - }); - } - - // hide titlebar - if (!self.options.showTitleBar) { - self.uiDialogTitlebar.css({ - "height": 0, - "padding": 0, - "background": "none", - "border": 0 - }); - self.uiDialogTitlebar.find(".ui-dialog-title").css("display", "none"); - } - - //hide close button - if (!self.options.showCloseButton) { - self.uiDialogTitlebar.find(".ui-dialog-titlebar-close").css("display", "none"); - } - - // close on clickOut - if (self.options.clickOut && !self.options.modal) { - // use transparent div - simplest approach (rework) - $('
    ').insertBefore(self.element.parent()); - $('#dialog-overlay').css({ - "position": "fixed", - "top": 0, - "right": 0, - "bottom": 0, - "left": 0, - "background-color": "transparent" - }); - $('#dialog-overlay').click(function (e) { - e.preventDefault(); - e.stopPropagation(); - self.close(); - }); - // else close on modal click - } else if (self.options.clickOut && self.options.modal) { - $('.ui-widget-overlay').click(function (e) { - self.close(); - }); - } - - // add dialogClass to overlay - if (self.options.dialogClass) { - $('.ui-widget-overlay').addClass(self.options.dialogClass); - } -}; -//end open - - -// extend close function -var _close = $.ui.dialog.prototype.close; -$.ui.dialog.prototype.close = function () { - var self = this; - // apply original arguments - _close.apply(this, arguments); - - // remove dialogClass to overlay - if (self.options.dialogClass) { - $('.ui-widget-overlay').removeClass(self.options.dialogClass); - } - //remove clickOut overlay - if ($("#dialog-overlay").length) { - $("#dialog-overlay").remove(); - } -}; -//end close From d8c17d160a1fd961b84ee3b6a929a6b80e0fe6f1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 14:59:04 +1100 Subject: [PATCH 179/215] Applying shipping method rules in checkout --- app/helpers/enterprises_helper.rb | 6 +- .../consumer/shopping/checkout_spec.rb | 38 ++++++++- spec/helpers/enterprises_helper_spec.rb | 79 +++++++++++++++++++ spec/helpers/injection_helper_spec.rb | 5 +- 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 spec/helpers/enterprises_helper_spec.rb diff --git a/app/helpers/enterprises_helper.rb b/app/helpers/enterprises_helper.rb index 85d1167ab7..5aa36624cd 100644 --- a/app/helpers/enterprises_helper.rb +++ b/app/helpers/enterprises_helper.rb @@ -4,7 +4,11 @@ module EnterprisesHelper end def available_shipping_methods - current_distributor.shipping_methods.uniq + shipping_methods = current_distributor.shipping_methods + if current_distributor.present? + current_distributor.apply_tag_rules_to(shipping_methods, customer: current_order.customer) + end + shipping_methods.uniq end def managed_enterprises diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 1df700a1fc..74145bbe1a 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -28,6 +28,7 @@ feature "As a consumer I want to check out my cart", js: true do describe "with shipping and payment methods" do let(:sm1) { create(:shipping_method, require_ship_address: true, name: "Frogs", description: "yellow", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 0.00)) } let(:sm2) { create(:shipping_method, require_ship_address: false, name: "Donkeys", description: "blue", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 4.56)) } + let(:sm3) { create(:shipping_method, require_ship_address: false, name: "Local", tag_list: "local") } let!(:pm1) { create(:payment_method, distributors: [distributor], name: "Roger rabbit", type: "Spree::PaymentMethod::Check") } let!(:pm2) { create(:payment_method, distributors: [distributor]) } let!(:pm3) do @@ -41,6 +42,7 @@ feature "As a consumer I want to check out my cart", js: true do before do distributor.shipping_methods << sm1 distributor.shipping_methods << sm2 + distributor.shipping_methods << sm3 end context "on the checkout page" do @@ -68,10 +70,11 @@ feature "As a consumer I want to check out my cart", js: true do page.should_not have_content product.tax_category.name end - it "shows all shipping methods, but doesn't show ship address when not needed" do + it "shows all shipping methods" do toggle_shipping page.should have_content "Frogs" page.should have_content "Donkeys" + page.should have_content "Local" end context "when shipping method requires an address" do @@ -84,6 +87,39 @@ feature "As a consumer I want to check out my cart", js: true do find("#ship_address > div.visible").visible?.should be_true end end + + context "using FilterShippingMethods" do + it "shows shipping methods allowed by the rule" do + # No rules in effect + toggle_shipping + page.should have_content "Frogs" + page.should have_content "Donkeys" + page.should have_content "Local" + + create(:filter_shipping_methods_tag_rule, + enterprise: distributor, + preferred_customer_tags: "local", + preferred_shipping_method_tags: "local", + preferred_matched_shipping_methods_visibility: 'visible') + visit checkout_path + checkout_as_guest + + # Rule in effect, disallows access to 'Local' + page.should have_content "Frogs" + page.should have_content "Donkeys" + page.should_not have_content "Local" + + customer = create(:customer, enterprise: distributor, tag_list: "local") + order.update_attribute(:customer_id, customer.id) + visit checkout_path + checkout_as_guest + + # #local Customer can access 'Local' shipping method + page.should have_content "Frogs" + page.should have_content "Donkeys" + page.should have_content "Local" + end + end end context "on the checkout page with payments open" do diff --git a/spec/helpers/enterprises_helper_spec.rb b/spec/helpers/enterprises_helper_spec.rb new file mode 100644 index 0000000000..1dc32d63b3 --- /dev/null +++ b/spec/helpers/enterprises_helper_spec.rb @@ -0,0 +1,79 @@ +require 'spec_helper' + +describe EnterprisesHelper do + describe "loading available shipping methods" do + + context "when a FilterShippingMethods tag rule is in effect, with preferred visibility of 'visible'" do + let!(:distributor) { create(:distributor_enterprise) } + let!(:allowed_customer) { create(:customer, enterprise: distributor, tag_list: "local") } + let!(:disallowed_customer) { create(:customer, enterprise: distributor, tag_list: "") } + let!(:order) { create(:order, distributor: distributor) } + let!(:tag_rule) { create(:filter_shipping_methods_tag_rule, + enterprise: distributor, + preferred_customer_tags: "local", + preferred_shipping_method_tags: "local-delivery") } + let!(:tagged_sm) { create(:shipping_method, require_ship_address: false, name: "Untagged", tag_list: "local-delivery") } + let!(:untagged_sm) { create(:shipping_method, require_ship_address: false, name: "Tagged", tag_list: "") } + + before do + distributor.shipping_methods = [tagged_sm, untagged_sm] + allow(helper).to receive(:current_order) { order } + end + + context "with a preferred visiblity of 'visible" do + before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'visible') } + + context "when the customer is nil" do + it "applies default action (hide)" do + expect(helper.available_shipping_methods).to include untagged_sm + expect(helper.available_shipping_methods).to_not include tagged_sm + end + end + + context "when the customer's tags match" do + before { order.update_attribute(:customer_id, allowed_customer.id) } + + it "applies the action (show)" do + expect(helper.available_shipping_methods).to include tagged_sm, untagged_sm + end + end + + context "when the customer's tags don't match" do + before { order.update_attribute(:customer_id, disallowed_customer.id) } + + it "applies the default action (hide)" do + expect(helper.available_shipping_methods).to include untagged_sm + expect(helper.available_shipping_methods).to_not include tagged_sm + end + end + end + + context "with a preferred visiblity of 'hidden" do + before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'hidden') } + + context "when the customer is nil" do + it "applies default action (show)" do + expect(helper.available_shipping_methods).to include tagged_sm, untagged_sm + end + end + + context "when the customer's tags match" do + before { order.update_attribute(:customer_id, allowed_customer.id) } + + it "applies the action (hide)" do + expect(helper.available_shipping_methods).to include untagged_sm + expect(helper.available_shipping_methods).to_not include tagged_sm + end + end + + context "when the customer's tags don't match" do + before { order.update_attribute(:customer_id, disallowed_customer.id) } + + it "applies the default action (show)" do + expect(helper.available_shipping_methods).to include tagged_sm, untagged_sm + end + end + end + end + end +end diff --git a/spec/helpers/injection_helper_spec.rb b/spec/helpers/injection_helper_spec.rb index 0eecdeeaf9..3b62fb2660 100644 --- a/spec/helpers/injection_helper_spec.rb +++ b/spec/helpers/injection_helper_spec.rb @@ -27,7 +27,10 @@ describe InjectionHelper do it "injects shipping_methods" do sm = create(:shipping_method) helper.stub(:current_order).and_return order = create(:order) - helper.stub_chain(:current_distributor, :shipping_methods, :uniq).and_return [sm] + shipping_methods = double(:shipping_methods, uniq: [sm]) + current_distributor = double(:distributor, shipping_methods: shipping_methods) + allow(helper).to receive(:current_distributor) { current_distributor } + allow(current_distributor).to receive(:apply_tag_rules_to).with(shipping_methods, {customer: nil} ) helper.inject_available_shipping_methods.should match sm.id.to_s helper.inject_available_shipping_methods.should match sm.compute_amount(order).to_s end From 6c5aaef86e954f8a64200b4921a12f4605ed5e09 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 1 Apr 2016 19:42:09 +1100 Subject: [PATCH 180/215] Removing ability to create order discount rules Issue with application of tax to discounted items is yet to be resolved --- .../directives/new_rule_dialog.js.coffee | 2 +- app/models/tag_rule/discount_order.rb | 2 +- config/locales/en.yml | 6 +---- db/schema.rb | 2 +- spec/features/admin/tag_rules_spec.rb | 22 +++++++++---------- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee b/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee index 888ec2b402..54e33006e4 100644 --- a/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee +++ b/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee @@ -6,7 +6,7 @@ angular.module("admin.tagRules").directive 'newTagRuleDialog', ($compile, $templ template = $compile($templateCache.get('admin/new_tag_rule_dialog.html'))(scope) scope.ruleTypes = [ - { id: "DiscountOrder", name: 'Apply a discount to orders' } + # { id: "DiscountOrder", name: 'Apply a discount to orders' } { id: "FilterShippingMethods", name: 'Show/Hide shipping methods' } ] diff --git a/app/models/tag_rule/discount_order.rb b/app/models/tag_rule/discount_order.rb index 9f293bfdaa..5984814289 100644 --- a/app/models/tag_rule/discount_order.rb +++ b/app/models/tag_rule/discount_order.rb @@ -5,7 +5,7 @@ class TagRule::DiscountOrder < TagRule # Warning: this should only EVER be called via TagRule#apply def apply! - create_adjustment(I18n.t("tag_rules.discount_order.discount"), subject, subject) + create_adjustment(I18n.t("discount"), subject, subject) end def subject_class_matches? diff --git a/config/locales/en.yml b/config/locales/en.yml index 4a9d8fe546..e38d3e3f6b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -137,11 +137,6 @@ en: invoice_column_tax: "GST" invoice_column_price: "Price" - # Tag Rules - tag_rules: - discount_order: - discount: "Discount" - logo: "Logo (640x130)" #FIXME logo_mobile: "Mobile logo (75x26)" #FIXME logo_mobile_svg: "Mobile logo (SVG)" #FIXME @@ -973,6 +968,7 @@ Please follow the instructions there to make your enterprise visible on the Open account_code: "Account code:" equals: "Equals" contains: "contains" + discount: "Discount" filter_products: "Filter Products" delete_product_variant: "The last variant cannot be deleted!" progress: "progress" diff --git a/db/schema.rb b/db/schema.rb index dc54bd8f3d..c8887ad86e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -683,9 +683,9 @@ ActiveRecord::Schema.define(:version => 20160401043927) do t.string "email" t.text "special_instructions" t.integer "distributor_id" + t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" - t.integer "order_cycle_id" t.integer "cart_id" t.integer "customer_id" end diff --git a/spec/features/admin/tag_rules_spec.rb b/spec/features/admin/tag_rules_spec.rb index a4e6e62d24..e91215a21d 100644 --- a/spec/features/admin/tag_rules_spec.rb +++ b/spec/features/admin/tag_rules_spec.rb @@ -21,24 +21,24 @@ feature 'Tag Rules', js: true do click_button '+ Add A New Tag' find(:css, "tags-input .tags input").set "volunteer\n" - # New DiscountOrder Rule - expect(page).to have_content 'No rules apply to this tag yet' - click_button '+ Add A New Rule' - select2_select 'Apply a discount to orders', from: 'rule_type_selector' - click_button "Add Rule" - fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 22 - # New FilterShippingMethods Rule click_button '+ Add A New Rule' select2_select 'Show/Hide shipping methods', from: 'rule_type_selector' click_button "Add Rule" - select2_select "NOT VISIBLE", from: "enterprise_tag_rules_attributes_1_preferred_matched_shipping_methods_visibility" + select2_select "NOT VISIBLE", from: "enterprise_tag_rules_attributes_0_preferred_matched_shipping_methods_visibility" + + # New DiscountOrder Rule + # expect(page).to have_content 'No rules apply to this tag yet' + # click_button '+ Add A New Rule' + # select2_select 'Apply a discount to orders', from: 'rule_type_selector' + # click_button "Add Rule" + # fill_in "enterprise_tag_rules_attributes_1_calculator_attributes_preferred_flat_percent", with: 22 click_button 'Update' - tag_rule = TagRule::DiscountOrder.last - expect(tag_rule.preferred_customer_tags).to eq "volunteer" - expect(tag_rule.calculator.preferred_flat_percent).to eq -22 + # tag_rule = TagRule::DiscountOrder.last + # expect(tag_rule.preferred_customer_tags).to eq "volunteer" + # expect(tag_rule.calculator.preferred_flat_percent).to eq -22 tag_rule = TagRule::FilterShippingMethods.last expect(tag_rule.preferred_customer_tags).to eq "volunteer" From d844dc8e1b4cea030e60529d83f17ecb8ddf01cb Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 16 Feb 2016 17:47:51 +0000 Subject: [PATCH 181/215] Bug fixes to Order Cycle Reports --- .../order_cycle_management_report.rb | 14 +++++++------- lib/open_food_network/user_balance_calculator.rb | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/open_food_network/order_cycle_management_report.rb b/lib/open_food_network/order_cycle_management_report.rb index ea238f7792..90cfa5390a 100644 --- a/lib/open_food_network/order_cycle_management_report.rb +++ b/lib/open_food_network/order_cycle_management_report.rb @@ -17,7 +17,7 @@ module OpenFoodNetwork end def search - Spree::Order.complete.where("spree_orders.state != ?", :canceled).distributed_by_user(@user).managed_by(@user).search(params[:q]) + Spree::Order.complete.distributed_by_user(@user).managed_by(@user).search(params[:q]) end def orders @@ -50,7 +50,7 @@ module OpenFoodNetwork order.shipping_method.andand.name, order.payments.first.andand.payment_method.andand.name, order.payments.first.amount, - OpenFoodNetwork::UserBalanceCalculator.new(order.user, order.distributor).balance + OpenFoodNetwork::UserBalanceCalculator.new(order.email, order.distributor).balance ] end @@ -67,23 +67,23 @@ module OpenFoodNetwork order.shipping_method.andand.name, order.payments.first.andand.payment_method.andand.name, order.payments.first.amount, - OpenFoodNetwork::UserBalanceCalculator.new(order.user, order.distributor).balance, + OpenFoodNetwork::UserBalanceCalculator.new(order.email, order.distributor).balance, has_temperature_controlled_items?(order), order.special_instructions ] end def filter_to_payment_method(orders) - if params[:payment_method_name].present? - orders.with_payment_method_name(params[:payment_method_name]) + if params[:payment_method_in].present? + orders.with_payment_method_name(params[:payment_method_in]) else orders end end def filter_to_shipping_method(orders) - if params[:shipping_method_name].present? - orders.joins(:shipping_method).where("spree_shipping_methods.name = ?", params[:shipping_method_name]) + if params[:shipping_method_in].present? + orders.joins(:shipping_method).where("spree_shipping_methods.name = ?", params[:shipping_method_in]) else orders end diff --git a/lib/open_food_network/user_balance_calculator.rb b/lib/open_food_network/user_balance_calculator.rb index 73926c1d03..642374cad1 100644 --- a/lib/open_food_network/user_balance_calculator.rb +++ b/lib/open_food_network/user_balance_calculator.rb @@ -1,19 +1,19 @@ module OpenFoodNetwork class UserBalanceCalculator - def initialize(user, distributor) - @user = user + def initialize(email, distributor) + @email = email @distributor = distributor end def balance - payment_total - order_total + payment_total - completed_order_total end private - def order_total - orders.sum &:total + def completed_order_total + orders.complete.sum &:total end def payment_total @@ -22,11 +22,11 @@ module OpenFoodNetwork def orders - Spree::Order.where(distributor_id: @distributor, user_id: @user) + Spree::Order.where(distributor_id: @distributor, email: @email) end def payments - Spree::Payment.where(order_id: orders) + Spree::Payment.where(order_id: orders, state: "completed") end end end From f8e3f29d1ace1d3c50e9a87119e6e2e46928996a Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Fri, 11 Mar 2016 16:50:55 +0000 Subject: [PATCH 182/215] More bug fixes. Spec updated to reflect changes --- .../order_cycle_management_report.rb | 2 +- .../user_balance_calculator.rb | 10 ++-- .../order_cycle_management_report_spec.rb | 4 +- .../user_balance_calculator_spec.rb | 46 ++++++++++++++----- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/lib/open_food_network/order_cycle_management_report.rb b/lib/open_food_network/order_cycle_management_report.rb index 90cfa5390a..e40c0f242d 100644 --- a/lib/open_food_network/order_cycle_management_report.rb +++ b/lib/open_food_network/order_cycle_management_report.rb @@ -17,7 +17,7 @@ module OpenFoodNetwork end def search - Spree::Order.complete.distributed_by_user(@user).managed_by(@user).search(params[:q]) + Spree::Order.complete.not_state(:canceled).distributed_by_user(@user).managed_by(@user).search(params[:q]) end def orders diff --git a/lib/open_food_network/user_balance_calculator.rb b/lib/open_food_network/user_balance_calculator.rb index 642374cad1..32cd00c90c 100644 --- a/lib/open_food_network/user_balance_calculator.rb +++ b/lib/open_food_network/user_balance_calculator.rb @@ -9,24 +9,22 @@ module OpenFoodNetwork payment_total - completed_order_total end - private def completed_order_total - orders.complete.sum &:total + completed_orders.sum &:total end def payment_total payments.sum &:amount end - - def orders - Spree::Order.where(distributor_id: @distributor, email: @email) + def completed_orders + Spree::Order.where(distributor_id: @distributor, email: @email).complete.not_state(:canceled) end def payments - Spree::Payment.where(order_id: orders, state: "completed") + Spree::Payment.where(order_id: completed_orders, state: "completed") end end end diff --git a/spec/lib/open_food_network/order_cycle_management_report_spec.rb b/spec/lib/open_food_network/order_cycle_management_report_spec.rb index e0e7cb40bf..6aa52abce2 100644 --- a/spec/lib/open_food_network/order_cycle_management_report_spec.rb +++ b/spec/lib/open_food_network/order_cycle_management_report_spec.rb @@ -89,7 +89,7 @@ module OpenFoodNetwork order2 = create(:order) payment2 = create(:payment, order: order2, payment_method: pm2) - subject.stub(:params).and_return(payment_method_name: pm1.name) + subject.stub(:params).and_return(payment_method_in: pm1.name) subject.filter(orders).should == [order1] end @@ -97,7 +97,7 @@ module OpenFoodNetwork sm2 = create(:shipping_method, name: "ship2") order2 = create(:order, shipping_method: sm2) - subject.stub(:params).and_return(shipping_method_name: sm1.name) + subject.stub(:params).and_return(shipping_method_in: sm1.name) subject.filter(orders).should == [order1] end diff --git a/spec/lib/open_food_network/user_balance_calculator_spec.rb b/spec/lib/open_food_network/user_balance_calculator_spec.rb index 9588439f5e..7ff095d83a 100644 --- a/spec/lib/open_food_network/user_balance_calculator_spec.rb +++ b/spec/lib/open_food_network/user_balance_calculator_spec.rb @@ -9,34 +9,56 @@ module OpenFoodNetwork let!(:user1) { create(:user) } let!(:hub1) { create(:distributor_enterprise) } - let!(:o1) { create(:order_with_totals_and_distribution, user: user1, distributor: hub1) } #total=10 - let!(:o2) { create(:order_with_totals_and_distribution, user: user1, distributor: hub1) } #total=10 - let!(:p1) { create(:payment, order: o1, amount: 15.00) } - let!(:p2) { create(:payment, order: o2, amount: 10.00) } + let!(:o1) { create(:order_with_totals_and_distribution, + user: user1, distributor: hub1, + completed_at: 1.day.ago) } #total=10 + let!(:o2) { create(:order_with_totals_and_distribution, + user: user1, distributor: hub1, + completed_at: 1.day.ago) } #total=10 + let!(:p1) { create(:payment, order: o1, amount: 15.00, + state: "completed") } + let!(:p2) { create(:payment, order: o2, amount: 2.00, + state: "completed") } - it "finds the user balance for this enterprise" do - UserBalanceCalculator.new(user1, hub1).balance.should == 5 + it "finds the correct balance for this email and enterprise" do + UserBalanceCalculator.new(o1.email, hub1).balance.should == -3 end context "with another hub" do let!(:hub2) { create(:distributor_enterprise) } let!(:o3) { create(:order_with_totals_and_distribution, - user: user1, distributor: hub2) } #total=10 - let!(:p3) { create(:payment, order: o3, amount: 10.00) } + user: user1, distributor: hub2, + completed_at: 1.day.ago) } #total=10 + let!(:p3) { create(:payment, order: o3, amount: 15.00, + state: "completed") } it "does not find the balance for other enterprises" do - UserBalanceCalculator.new(user1, hub2).balance.should == 0 + UserBalanceCalculator.new(o3.email, hub2).balance.should == 5 end end context "with another user" do let!(:user2) { create(:user) } let!(:o4) { create(:order_with_totals_and_distribution, - user: user2, distributor: hub1) } #total=10 - let!(:p3) { create(:payment, order: o4, amount: 20.00) } + user: user2, distributor: hub1, + completed_at: 1.day.ago) } #total=10 + let!(:p3) { create(:payment, order: o4, amount: 20.00, + state: "completed") } it "does not find the balance for other users" do - UserBalanceCalculator.new(user2, hub1).balance.should == 10 + UserBalanceCalculator.new(o4.email, hub1).balance.should == 10 + end + end + + context "with canceled orders" do + let!(:o4) { create(:order_with_totals_and_distribution, + user: user1, distributor: hub1, + completed_at: 1.day.ago, state: "canceled") } #total=10 + let!(:p4) { create(:payment, order: o4, amount: 20.00, + state: "completed") } + + it "does not include canceled orders in the balance" do + UserBalanceCalculator.new(o4.email, hub1).balance.should == -3 end end end From 9b5bfdeb00ea9d099c27efd07c07c07ae173cf94 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 17:01:30 +1100 Subject: [PATCH 183/215] Allowing Order Management Report to filter by multiple payment / shipping methods at once Also switched to using id rather than name to filter --- app/helpers/spree/reports_helper.rb | 10 ++++++++-- lib/open_food_network/customers_report.rb | 3 +-- .../order_cycle_management_report.rb | 6 +++--- .../order_cycle_management_report_spec.rb | 16 ++++++++++------ 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/app/helpers/spree/reports_helper.rb b/app/helpers/spree/reports_helper.rb index bbc184d800..aaed402a6f 100644 --- a/app/helpers/spree/reports_helper.rb +++ b/app/helpers/spree/reports_helper.rb @@ -11,11 +11,17 @@ module Spree end def report_payment_method_options(orders) - orders.map { |o| o.payments.first.payment_method.andand.name }.uniq + orders.map do |o| + pm = o.payments.first.payment_method + [pm.andand.name, pm.andand.id] + end.uniq end def report_shipping_method_options(orders) - orders.map { |o| o.shipping_method.andand.name }.uniq + orders.map do |o| + sm = o.shipping_method + [sm.andand.name, sm.andand.id] + end.uniq end def xero_report_types diff --git a/lib/open_food_network/customers_report.rb b/lib/open_food_network/customers_report.rb index 820bfff7df..d7900a16ac 100644 --- a/lib/open_food_network/customers_report.rb +++ b/lib/open_food_network/customers_report.rb @@ -31,7 +31,7 @@ module OpenFoodNetwork ba.phone, order.distributor.andand.name, [da.andand.address1, da.andand.address2, da.andand.city].join(" "), - order.shipping_method.andand.name + order.shipping_method.andand.name ] end end @@ -78,4 +78,3 @@ module OpenFoodNetwork end end end - diff --git a/lib/open_food_network/order_cycle_management_report.rb b/lib/open_food_network/order_cycle_management_report.rb index e40c0f242d..8a9c6ba60f 100644 --- a/lib/open_food_network/order_cycle_management_report.rb +++ b/lib/open_food_network/order_cycle_management_report.rb @@ -17,7 +17,7 @@ module OpenFoodNetwork end def search - Spree::Order.complete.not_state(:canceled).distributed_by_user(@user).managed_by(@user).search(params[:q]) + Spree::Order.complete.where("spree_orders.state != ?", :canceled).distributed_by_user(@user).managed_by(@user).search(params[:q]) end def orders @@ -75,7 +75,7 @@ module OpenFoodNetwork def filter_to_payment_method(orders) if params[:payment_method_in].present? - orders.with_payment_method_name(params[:payment_method_in]) + orders.joins(payments: :payment_method).where(spree_payments: { payment_method_id: params[:payment_method_in]}) else orders end @@ -83,7 +83,7 @@ module OpenFoodNetwork def filter_to_shipping_method(orders) if params[:shipping_method_in].present? - orders.joins(:shipping_method).where("spree_shipping_methods.name = ?", params[:shipping_method_in]) + orders.joins(:shipping_method).where(shipping_method_id: params[:shipping_method_in]) else orders end diff --git a/spec/lib/open_food_network/order_cycle_management_report_spec.rb b/spec/lib/open_food_network/order_cycle_management_report_spec.rb index 6aa52abce2..2ac752561e 100644 --- a/spec/lib/open_food_network/order_cycle_management_report_spec.rb +++ b/spec/lib/open_food_network/order_cycle_management_report_spec.rb @@ -86,19 +86,23 @@ module OpenFoodNetwork it "filters to a payment method" do pm2 = create(:payment_method, name: "PM2") - order2 = create(:order) - payment2 = create(:payment, order: order2, payment_method: pm2) + pm3 = create(:payment_method, name: "PM3") + order2 = create(:order, payments: [create(:payment, payment_method: pm2)]) + order3 = create(:order, payments: [create(:payment, payment_method: pm3)]) + # payment2 = create(:payment, order: order2, payment_method: pm2) - subject.stub(:params).and_return(payment_method_in: pm1.name) - subject.filter(orders).should == [order1] + subject.stub(:params).and_return(payment_method_in: [pm1.id, pm3.id] ) + subject.filter(orders).should == [order1, order3] end it "filters to a shipping method" do sm2 = create(:shipping_method, name: "ship2") + sm3 = create(:shipping_method, name: "ship3") order2 = create(:order, shipping_method: sm2) + order3 = create(:order, shipping_method: sm3) - subject.stub(:params).and_return(shipping_method_in: sm1.name) - subject.filter(orders).should == [order1] + subject.stub(:params).and_return(shipping_method_in: [sm1.id, sm3.id]) + subject.filter(orders).should == [order1, order3] end it "should do all the filters at once" do From 0ed97d820c4f2de5c53a45c304f6c0fb40e0fbcf Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 17:03:23 +1100 Subject: [PATCH 184/215] Removing unrequired Spree::Order#with_payment_method_name scope --- app/models/spree/order_decorator.rb | 7 ------- spec/models/spree/order_spec.rb | 27 --------------------------- 2 files changed, 34 deletions(-) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 5fc96c34c2..8e3c6b7bb4 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -70,13 +70,6 @@ Spree::Order.class_eval do where("state != ?", state) } - scope :with_payment_method_name, lambda { |payment_method_name| - joins(:payments => :payment_method). - where('spree_payment_methods.name IN (?)', payment_method_name). - select('DISTINCT spree_orders.*') - } - - # -- Methods def products_available_from_new_distribution # Check that the line_items in the current order are available from a newly selected distribution diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index e72fc7a1c4..ab86be1580 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -489,33 +489,6 @@ describe Spree::Order do Spree::Order.not_state(:canceled).should_not include o end end - - describe "with payment method names" do - let!(:o1) { create(:order) } - let!(:o2) { create(:order) } - let!(:pm1) { create(:payment_method, name: 'foo') } - let!(:pm2) { create(:payment_method, name: 'bar') } - let!(:p1) { create(:payment, order: o1, payment_method: pm1) } - let!(:p2) { create(:payment, order: o2, payment_method: pm2) } - - it "returns the order with payment method name when one specified" do - Spree::Order.with_payment_method_name('foo').should == [o1] - end - - it "returns the orders with payment method name when many specified" do - Spree::Order.with_payment_method_name(['foo', 'bar']).should include o1, o2 - end - - it "doesn't return rows with a different payment method name" do - Spree::Order.with_payment_method_name('foobar').should_not include o1 - Spree::Order.with_payment_method_name('foobar').should_not include o2 - end - - it "doesn't return duplicate rows" do - p2 = FactoryGirl.create(:payment, order: o1, payment_method: pm1) - Spree::Order.with_payment_method_name('foo').length.should == 1 - end - end end describe "shipping address prepopulation" do From 44077a2c6f5ade746712b9b0c18fab61c2452409 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 Apr 2016 15:43:55 +1000 Subject: [PATCH 185/215] Fixing styling (bottom margin for save-bar) on inventory page --- app/views/admin/variant_overrides/_filters.html.haml | 2 +- app/views/admin/variant_overrides/_show_more.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/variant_overrides/_filters.html.haml b/app/views/admin/variant_overrides/_filters.html.haml index 7330c26799..c9f3a27b32 100644 --- a/app/views/admin/variant_overrides/_filters.html.haml +++ b/app/views/admin/variant_overrides/_filters.html.haml @@ -1,4 +1,4 @@ -.filters.sixteen.columns.alpha +.filters.sixteen.columns.alpha.omega .filter.four.columns.alpha %label{ :for => 'query', ng: {class: '{disabled: !hub_id}'} }=t('admin.quick_search') %br diff --git a/app/views/admin/variant_overrides/_show_more.html.haml b/app/views/admin/variant_overrides/_show_more.html.haml index ad943c853e..8d60593ddc 100644 --- a/app/views/admin/variant_overrides/_show_more.html.haml +++ b/app/views/admin/variant_overrides/_show_more.html.haml @@ -1,4 +1,4 @@ -.sixteen.columns.alpha.omega.text-center{ ng: {show: 'productLimit < filteredProducts.length'}} +.text-center %input{ type: 'button', value: 'Show More', ng: { click: 'productLimit = productLimit + 10' } } or %input{ type: 'button', value: "Show All ({{ filteredProducts.length - productLimit }} More)", ng: { click: 'productLimit = filteredProducts.length' } } From b050f27a843cb328b61c5a21475596bf55f10566 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 18 Dec 2015 09:49:51 +1100 Subject: [PATCH 186/215] Default logos for new installations --- app/assets/images/ofn-logo-footer.png | Bin 0 -> 7748 bytes app/assets/images/ofn-logo-mobile.svg | 80 ++++++++++++++++++++++++++ app/assets/images/ofn-logo.png | Bin 0 -> 9749 bytes 3 files changed, 80 insertions(+) create mode 100644 app/assets/images/ofn-logo-footer.png create mode 100644 app/assets/images/ofn-logo-mobile.svg create mode 100644 app/assets/images/ofn-logo.png diff --git a/app/assets/images/ofn-logo-footer.png b/app/assets/images/ofn-logo-footer.png new file mode 100644 index 0000000000000000000000000000000000000000..f612d5aa87f3ffaac9267bac1984ca83efc7c58d GIT binary patch literal 7748 zcmZ8`cQhP8*Y~bntiF0@mFP*3AP83PqD0v!(W3VfoyB66uzE{Kh?3|vY7%|*5>^RO zg6N|6cs=Ji-}A?J&zzZC=FXhCXU?79%*5*IsFD#g5d#1KGIcd2eEOa(+&Ub(9xG65}1%Dn}hMZuo853ZlXMzon~O2)o96 zLi?_gY_puRDb;QE*qW!r{RIKyh^$1*)scj1Vb&qY;8lDuroXdp(98R`h>SzjFoc9T z$zw0`Pxe|y?NfpN?(COzy^)kw0nBFfL%8W0Vrk80iyh`mX7_6F`9ah%dSsy)1$p;Ra+Z+|CH3XJ>L}{)dq|%#>HlJrD ze;S>wSvh}cWQ8z<@H*41AHCq}tqlEP)WE6q%H(|M)lt(&86%rLM5%G zf|G(bE1Ok#m(JRqPa;zBjgJQ>XVb!&;`~gOioTB7X0Ep~P%Z}(|FehU%BPCW*2ag9 z*!wVFXCs3BiE6O@aldA{$tM%ihX|Lq%@#Y*I~Lk!Ps2q%n{pCjxO|d{K9D`;;Qx)* z!HE<{I_eX)7z?v^Pc{B?TDC|)%YRc+MSATjAz563BI>e;U|er^cx6yJ|2ll#fWHk3;?|9LEIuPz;%4BE%+lMy^wPW(@Zy`lzRYBZ3@8J6qq88 zfZh)L5n`oN5{A0;$2XjqnUqx*4#U3_Zaexbg4vd8)P z4`C5e_SHh%I3Y*)fFN_I2QOTfW=&IRBROAl_bJ|3asjd&;|b88F?_-09Z3qOa~4bc7B^J~$mAd1y?U+Q>M zx+8P9bXPb=FcA2gi!)=slQ3u5W>{ioz2z~Rjh3d8C@h){`jZCAd}Pc~w@fm;1ZwCH zQd~EEjLFa@s?qb+Ca`@^xLQhrMZYblEWRD*rDSw)QpN?RT1dCtT<{d%?pVB`mg7y?DcThIF_Z>Rc^3F?oe&c1BoQGb4> z4Pew8Z3B^bJ;&$H(>?jgAOz#Ge$W3%V81&i9@G2`PENq>)3JBuX=9JvX!UVCVs2{& z!kV!4A#~e)eOvg@{j?nuxDY?==fb%MWq~_6Ru6i*W}PhfV5loLLlpj5dypP_KR~`ep=zTIzDKYM19Y$CDU)I-@v(0%JfI3JrTdJdEFi1~A=Xn7-W!da zcM5?*<;H*MJjb(zfxAUv5O{IWk=_;Ib37#)g4L91TR0MFCZM(Fx8ybTkIEOIzFp2h zj3Nc45Ma%>{DLmXd;yGcAkn--b5aN?hIuC`F1~WZ)(~jRM}hUYd`SQXW9MIKD#xU>mp#Y-ERmcrh zwE@+qwQ=fgBCyl1$gEllg(S%_Zr2!(Cp%SCYl+FHH5| zD+grBf(2}``a*em{RZs^PRh4G9Lce|jL;n`0qNf7`~ArB<*m(lApZ&8OSVmY0Zq7@ zLV*UGzuPVlv5iN?8fd9f@WSPq9ZmYUH;*%R?2Bp!jVNc%JgPhH$8;WWHWx1~c79o@f;4u5#FBaa#bGH54}Ufz z;^t7OKhx2Tem($nDG6FxJA>()NqN>*0@BuyHXz>nkZL; z+aWL;HbY-ve^z-sPZjWra;H@QPpc(QA)ttq+wV^qV#aO`j+#T6oeT!P94&qd3$1~| zaV^O5UwU&JcE7g?zwR8kaXN*5fAwZ9hp&s`jml>*2T2W44SJK^3C{}-sPP4M)N`>F zdK9@v%P*uY;cqY75ZNb9DX2hRWiz^6YKI|7eSz{07dgP`tV~91_hnWgRrLbtE)hlkZ=#b9lBH+?ETJ+c!3SK-SPuKX#T+5psB%S2lM zYNmih+PEBfW2ZWtFSt~@vP3RDfu*>!*#c2`|4YyCKO#&6BTSJK=1c&y5L^6V{y6oC z%`d{onUgu4B{;1#9#r{$werxvi;mr~TzKZ3(Y`Xz;Tv@#(`#Gv3M}zEa5&LUWcDi? z!3)-U9lh7{!c$Tgt7|necDG^7hk{u_3DAR=G*`VJ!#`?g$ZHv)DMfgO8B_=#DIOOc z&qWC^m$(}y$*LP?%keR$ilyiro9PxBUbO2({ruWKxQq~*`uK4`_qgMJE4_ym+(Z=6 zAG9Wz5>4`|5c5USBC#k@O$wwS+F5`=#pcUtDpSO7z`qxG4iCxFX>w)S@Wdg--Vx23 z08O9+)8n~$0|R8oCniGf-1v1;IYLpNH#3|}KqgE3KiW#I3N)eVO71pr6Dq?hF4YY~ z?(C$VSc?n|4s`MJwv^49Sn75m^??-nnv~1ad-0@VS0+Ma^JEh{7@ngV6G9Uv6pVQj zZYbf7Pe(3)>0ySAfLah)0ei{t6)}e&_J`!}ASw#Bc0DTmvrj1Oo`$qKm*dXp9SBnnq}tTnqyB;ujb;XrLOs7eKDfEDwd$9=ce8kMNzJHT z|EPVES?rgqtBl{L&idQFyhPiG1y|+ri;AkgDTgIipZ49K!)!TlttI)3>%Ze9A22O|jKRDf$_+t6I#{zYIzvp@ z>mu;j8qQhi`ppYkOm%IKCEL3&@uuSW8Jm$#8}XTu)S}fe?d+5(ZAVF&oc9qUK8t1aIAR4RG%rT?P8xKdlI8!=2=jDS(0;^Ub~ ziDRZ{!6+G=os$93{VOi1qG%j)X>JavoOJuAI(FCIcc zF8)P&&r~{JBX?$SXBA4D7vs%B`eFpD-+?LqT{0^vx!;*3=7?oH+#)=`&9N@7wMLy# za$3ifoD8epx;(uy`^*6=CV<(jJ|Sm5KV=x4b~gTIU>96jBqznVz(__Z9a3kLzvZcz z?aKvEWCJjY0-8@>cpyJz=R0<&x++z;{HEfw4Qy%qA*U!GeT%SKF^HB(lz#P}@J@4UVHZ`#G&_^l%@^^lN@Gt$J&X&4QsJW-%CW`kN?B zQ50rBOwbMJlvk{Oz2(x)f;{e1ZL|@)ac2o+na!+g7{{YJa&ZFH236MMS`)o4*I5e+ ze{q$7kmV)LD8GxIw(wny{&=O#+YFF5!v`?;=}ILbc1ut9z}M;{dTYljD#nHiF#Aky znOM!31QqKe)UfrQCCKd-|DiYU0rnxxxk>yJ8*KR%zFhwwEf&7PmY0f~{sEVp)Sam; z8{rh`y8c<_UU@+5_L6}>90_k&7;v_0yW<3K5OPZiTy4{W9C=mdN8nipBaiC<5n^n0 zZo2z43&KUO6dxUupp8YGK%yvs$Rh0gL0t(m0tzD}#-zS{jn{qLNjTt-wTsE=!o&~PD@n0?ZMwlpKYDu|e@kfKE1>|6jfz_zb*Veu9<%aW4^~9{Oi`yL?EYe6 z!V2}1uR3oY*_GNon5Pn2=?5wK)WDnM4=@!H+{>mkn?-gy+lb0tzRaov_>zmAar zCq+UmKS9_(;-C2J-jNQCOfXBe;t8Zb9Z4BT6ddPrgM3R%%|K+UQS)LTL@NWV+sAd-Nnjlk+fRLXn%~hXo@5pQ8S$>TobWhVx~iXL;-8M?#ju8D zzVrv+tR(@6D-+nq6G=5s9p?9$$t%bwUB54pO*o-^W4Y_$5aGZ)Vzf-*8JXHc5)@{w zEYL0$VqB6 z5mv-Uqf6}!;VOO!dx;a&wGEC)vm3r~diDo!XxW(Nnw_gsds!^Nv!{;@jtej-q7{K*4knmb!ces_)|tKzbAkc|zJ`xle|-3p9kf3es}1cY5d!5={i z$B{-FQl_F+O~Uh#Bnb4kOXwIK)X!jCH$exCJ{Ymu+2{%^U|ZEsNak1lVAGLN47BxN zqnZL^79^s7G@6={V8-QQ`o;!dsNuuoE?#VkNPX84;DqbMM`;5*r_ixSUl`oAo`KPf z3P{p+?w~T?#9ssCw4t*EbFVe=v9U-VWSd=Xg+1e#jk^ZmFemYaJaXsVq|L=(d3{On z*{$pkgAf%TD%jZ5Q#csQ|KleZGu;}8?R@`DBTO4#Ny$jl4Cz0*>PQZ0TTWLvdps1Y zN*A1o-~AU0s7ds;&&Bg<|E-xOdkN%kSTh3q2ML6Wn+wIPX2$WY>W#H@tNH3l@c{#- zlEsnbqZX-EAuS>QD5LKHAhbwspK>Dtqi~{4?3<&OQWrJ;hipMPog9-8yl8j^Q8=%F&d*H}F%H;he3UC7;A;hXCv6ni4F}d( zWF8^^Jk@S2!T&P2+2qQE^brpKiw>__aMC@!RLt;swaBTtArz!c4Of zbB88N6BZ0=E1lm>qcgXNrqjc*Rw5GO)HMf%9+lZvGRrD&^6c?s-0jb7%p;hMw}Qp7 zt>8^m5Bc+~kjn(kf}2J!;}gK_%{y5^pmo>{O-Y{@kH1gZKVm#30N|jKj@XYwt7{E2 zQRC!x@q1-2%j+bWDsgCBT|P`4_AEZ?4d9}^sxH;K-uR)=sB=~PU)9xwFI;SY;(JkP zMrq#L6a4SelDH_o&3U@w$C=|jjLxQm25pthLNRI&Ef)PrKME83qDzR}vb=(8X*r_I z$wJI6#(etn?1h%8>yv4cr;G0HY8!jhXQ($2_R6LVPC6I^FIz^PIvMxqJh3lw>H zh1>!ihWr-s;`tEvWZb39AVv`hq1WT|DR{=LprNuU+R|T8TqmZep0SEd$WytO4y@_* z?zp_Rr-si5Jvs}h?|8#q>Ds{gR>irNMIQkpeERi9eR7lz5X`3SoM0k6Yi5vj7c${M$4s8~q{v!+ zxQq;d$smb#)EV`1MO45Ex)lw~L88+Hwhp9NhFLOl2SfX|>HS|Sq*ibhcRxRHWJ4q- zK|o-hE4cvYI*Mo#D9Y!B08?PPbB5MC{Op!q%lmX|`R4~doEoUC3g z!BU2XX)jwT14KR{%kOHd6>zEPDHBU6Yc5(@r;(x1I?$17b=$K@h z^ty&1p;5`2)T7IRq_4~Kef|DdIQ;YrkoyPHUyj%}*C&Kx+kh3$>S_^odF9KPw5mPZcv<61Z%4pC|4Dfn49pD=gkfa3Z^ z0Uhqq-%tps7ARALzh~uIzAHo)T#hL@QX`{hQznI^$MhUFDwv$sl$ylRTfrayrPtcA zs)6QUB5B+FK3JE2v_keEying0 zw6e|E@hlIYTWb8^`)i+EwofF1;qpB=RGPR}(F*!fF_d@FK zMyf;cpH2C#;cbhI3m38n-uB@;MkPMN@# zSAt|oZo&lM;1>`%-;8eIo8-{mxcC3sXo3N#lli zMRhg!In{7q+C^ZPj1}?cK}O~+`_OeETOK9QV&BX}Zt5iWXQh5?Nv{T4 z^*`pwBaagZPx>GOeN-tzNut?&c0DP{ew)ACPk<_lCQ@H6++@sqNw&e82sdHMh6;2& z|6X;82%n!pR@aMiXf|!Qzn+8`Jt@=u{GH-A?{Ertzo7fS7f^R~Vpi^Hyodeq8$x7- z7$gT#=EseRy=iXRCHn(!J9V1u{0D)__t|Q`2NhbzH7g+S9{hggXr*A50OtxcE4**J z)?y3l)Yl;PsSQ1B@W^=^ypNABQ$<;8tCn19dHR>~*G}l`hK#bKd$+N;CZ(fpev6F6 zx8*NQ!-dqdKagOko!l>m$mvL4y_F3!%BDwu?9$w`Y=!BO<9eB05d#|UTTP9GWIoW$ zmi04_bgUT333cn>e6%I+;R`$&b&C$wEt(kV)bH*0ipCMQQYHQdu^^XLnB2q%6qalQ z3N69hP4qXQ9NVrQu^r1AjBTy(DNj)h7VH|xceFz#hoGb46+B#zVn}gR{LAqnyOOMj zP})RDNylhXBHU;7U3hqeoM*0m=1hk*hj%T2b_J^P7?s2?o#GevP|h&J8qXrbzg=L@ z*<2vh-s7utr^EHs?r$ccj0n+^lRy>vMZ3St?nV-Yr#>~+G!R8tpQD41{XC=DUlMMd zpD2V7Ce&@EHXqZsJLAi;6}#L;NgHK!ei2YhLl*;}83jA3$38$LsG_6<7M(nn$4GX8op!LWi0S>0L_CVh&-!1~&H7Nq} zDxR_++>vWIxVo)y!(&XEqu$F-*5n_ULsR$?5^$4pVR->UX>lBh*o26$N3JbEF4XLX z)yr_yM;(Xs5cTq$S63Z}st|QVJ#L6_@FfrTibZpIA}+hXLfvhMP}4cQ8z*w+bjVd? z+v#<6?VC5*8C+eTxs!u{%SjfkkqR?<8BqE(e?=9wXd%`WZ7hxwO&yD9CS7f0O#->N zjr3)0CjALiC77qb)E#qqTW_07m{PBFvB}c?lEZpM+8U(Jbs#LIy_&+O4N_+qS literal 0 HcmV?d00001 diff --git a/app/assets/images/ofn-logo-mobile.svg b/app/assets/images/ofn-logo-mobile.svg new file mode 100644 index 0000000000..7c48b00b1b --- /dev/null +++ b/app/assets/images/ofn-logo-mobile.svg @@ -0,0 +1,80 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/app/assets/images/ofn-logo.png b/app/assets/images/ofn-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f53680c342365bb4e1ad7724552c00e09fc2555a GIT binary patch literal 9749 zcmXY1by!sG(_VT(k)@UHE=g>^gi88YPtOF6ovMq#F@ok?v4Lx;v!1VW}m*!~6UG z*=w)ebLOdeX72l*iBwaS!^I)P0f9ic3i4155C}C8_#Ont1U{$Iy-+|P8ju3?wU+1H zLDr!CntW>U632vdh(@`$AcB~%+P=_vDRAcw#JY#cVT5A0i)w2U_yK{#fmW@Yb%L(UeeTJXjWmJ#@c^fj4f(^dcsgB{Fdli>Gu<@)nnkDm%hu6*n$oAmf%jFaUI= z3wn^be-J{}V=tlHvOYW^=lSq`|c7@}4doRYK^?sISm?5+9bBDt-MhA-KP_ ze{afwX+t15%NIq%C_I?QW>~+cp{S;~%O4@|cY=(-t08Kj=A<^-K-NH|^@KJF=@$$k zfJKa-XeTbP>tZZ-mG__(8bi+6mPb3%DEx%&-y?E-mZt?VAgG?tLw+Qy#6He|sKffC z8D-hErdN+)zxq}*p@J>U^M&!fOq?0R(l;={<4CA8l~;{){)b^zq$pQ7G>6ty*#nXs zyfMRqVtkXYxQlZP#x@}qfrQmJU9Kl2xKvo!Q-ijW^KGFTpW z*_%6%F5oj}yLXj^TH({7)=2cA-pAj;JJlflo~85e6%n*D>(rxSLOhs`s@RX~kUv5n zQM=awks4yGbqkP3p*JNpzhl*?&6r6)`K4EYH>C+7F_$x3$%F(98dd=96a2&PK{x4k5E zF2*Qzc3HZnZjJ!S5YmonsAb)t+J(RY(xUZGLiMNHFGm|ajsbD|!?UIV)GC+L>Y|jN zAks~cj)9DZ5xm-gYXX7(mPq=s%lekuHwM0*qNlQVF!SS5xeXmlx&=4N4wWVHOy2!w zR(kwPBc~AKypp(YsK`iGjD=j2TcKg< z8F0YKQT;;1`bVKp-9wwvR+I&==1=f_nHY%={(dA7%=;k3gjj9~E6I#h?(UONG1keX zxBeCqkM@lf8#>*%c;K?F3*E2mtalyn`IS15u`jxYBs*u+TmW_nh?VC*Tm3$WR5%M0 z?)11asu?cjhzf3)CEGmLjcCKVOAc4+eAXWSA|tg>WsmBaUw{N|P*;`Q<$|?#Zn}8} zQ>9UUa(Q{O&I=pk-M^>x+T3g+!iEJ}@NOQ>nczDd1triM;%@(0Mmbmm83?$?RR%fJ>O$Oi{2(F zDMz(IXs@sdJFV@J<4(yVW;M4r)hxMH(spv|hB!e|pVN%1=6CzP>RKS4Km@i1vMv z3$8#wz|_ggZa%;2aL&$29Kkd9$oW%a0Y9Bsj`q)C?lbNI<&cv}KjYT-q;lX&f{w6l z69z_w3)+CsJQ)aWA~`=%uJG{(X5Gw9IMVaJxfbSR!K&dGW)NM+4n7flc41L>UWp7$T=IpP6 zX?0M+ekzrY)L3k98<;2!X#K0}YzU&CW=Des?Fzm|H{iLdcQ_62qVC3RROd}})^*ym zQXYPiobJ{l9QlaUFr9Gklb%vP^GcG;6LE6=7Ui>( z#XW|rcEl8+^zPN92AR9W<(qsx75qAZI<5GolGaqOq+#4{rzEeW4|ucnc+SJ63?FcA z$~x^%42I|KEntO5Lj2PSHI41`-MMUeP?Kh^Z0NMO06~5U1KGhp9S!%VjfayYpar(i)Iy}MhknqSzOXJ& zD$nZA8Mq-oFu2#k4rzmDRDaG}#$*)^L7Mh{665YiQ-nCF<~~36@DH&uBjKU_qYTQc zsIMJg$LlSdggwhQ>N8P5braZXF|xt{u0hQ8hn@yGn-|o)*w=iSZ~`0j>%{tlhFO^` z1?ipy9MgZt4hw<>?r3l|>R4qI!~MN{+~1S6vJGK826d%2+5D0&vlBve7>;>x{?Y39 zKYWD8VP^p7Q3H{O2jpBh+BTUd&q}1`5xS1%`az9d5w{XQzt=ON9=mrk&Rw`2g|}RG zt90^6>Y~tw@IRqB`@y=;A1Q^SKHd9aBD@f@_afHXe?LO)=+u+oL)a&u#b;UBQA-?P zk}dJr+dHXEMmXNtVrpgMRQspxD?DRVX?ek4yTe=)E7$4#>yX{T zG_y?=SzcX7lRxP;wRk9ot6R2%@3OWV=yz;N$MqPqG8S%1tvr`xIeL*agdNm1ce#aqO{$_I*n;U7)h0rDYm5#+D3tc+EeZj1CogyYu$IjvXIJ{8?5Wlt*63e@ zKN55#6e-&#iM)vLiANeR_BajGVdfZsC)z}vllIQ?HtODlg_+08NPN>zpAx&cLpH#g zBMs~;UQx-pNEo%3ZO4Tx(1gV6``cY1CFs~yb-TxS>eZRu>Q>3~M93;Xn@yK|#^nT) zBpeOf95o&dR|J`!sEvPrmS@s*z)rnEEw*yZ8!#_n0&>LjQ&Fqv+A=8fVr}~vzkg%o zmwkPITe*UW9mb$7R0r+x@=G49eBG583G3svY5yXT8@|m$uQqjCegk@=^l%?{y*)Qn zU=L&$tHr`nG@tF)J_a`H>&DQd*Ml8jM651xr>iQ|x%W0^1XYw1%;Sl=wL#ZOvY_rZ z$p`SVek_S1!y(nR+3K70qv=-17yx&~v_RD+@BeaMd|NYuk&{Ku><-ycqCc5JVF=;J zSTP=MGA;I z#%T=yUF5b3z`ogo7Ow8aG>kDb!|Y_mjdP|^j%^WGZIS`tpsr%2A90AkyjRYMWD8R*U8g)RtK|QFPvL z`E2?JiBOBug>)O#br}QJ)4cTOl#_fa;K|>xy>@?#Ur>Jl-#39M>23OaspF7%& zYr5k;yFVd zIWC!HsZQQf_c=O2I8u12k4IOpi+Z-~|G7JY{k{}%eYS~QQ^aJY>v1Uh7sFZ6RePWm zynjUD6_hww8>7}owD`^kTfHxvIV3*zkvoo4Dw)tadIz^|B=k8;4Dk0V6{N+7CQ9iR2w2Adhg+W0aU9+w~CW@s>ye=H5C>+ zMOBgLUfQ2tfl1`FuQDK2qKjHR1M(8vypluTD4@>>p5&+_T-QcZx(i{qD>r|3G z6V#*UMsZQg3emXaSn*|gH)H{ltYzOdd^h$39CHz-~MoedHr8>0I7M zl*RBW8fA7FVaHLkNT4((z%H=8Vr!ZrO#fKE0iIL?UB*V|q>4v7(eyGTipTo(r%c$k z{BbIOP<*sQPu#&P1H*Ahc5}QDIT{(gwf)h}#$IC>c_nN+^hknV5_cTUEaDIwVVeO~ zd~`2^?I?4gl-y3Hh&$f^V`IVLrjcq51`)VEF%;=#U~E^v`gL^j!Y|;EXq5^*x@5zx zhD+MkphGWiyxg-_9tFyIairJs^N-+<_GEbM!BW*vKHBv{^y4=ZsV{fv{R}S3RA1$D zdE_Stp8R}UYc}5)QgWMwP2;JPWV|AEzADm@61TC=5yKl@?1e>0i zHpKd9-vbo3`TKUny03cMdGYHy^61W+dodJ8QuOGem6L!0yaDiQ9%F~m$(UpZZaVz80`M)Co zi+_pT{f>>8TBxeuQXQ^eW(O+z?I zOH#(z-F)pERpj8^Sgzz=Yz(aq3-Y4#n06wk%>6g7*c9Z(9JAKO>i&h1MvAPkQ3rnq zWec7WMJEF)aUMUkv@FyoY%oP!)8bfBfsVB*s+j^+eEfDmPs~Df z2}}4$rJztDmf=hrM7fmnnLJ;_E&^caZ9B4$WS#~x+*fZ&fl+hjlJb8wJO2CC; zm?I}vzwt}YQVj!i+D%a;?Rm9R3nm=mP%e?RwftuBAwD6mw)>CB1WR<-Z`i%jYj zSXX^hPX~{$F5a>;ziMS$aZ=>QR1Y06ngy#vIOvvga@NjU{s5@K z0T+YQ4s!gwW8iY@4V?|F<{UV=y}Bx<)61CYQG-6}|9n+aK+@yF({_#1e!gpOE*NI@ zl_qCi?=P9w~4s5kB4WZe`A{@b(a1X zIeN4f0fnrh0RW0h%Mf`h=j2~D%FhrPW9_ja@hTLLj1hAdk3yvX_?&ZDtr!J+G+T4|Mv2i?u24fF$e_8RA`^~sh;M^@x%KBX?6!`6}dLWjXXBCii zR|q@>ppP%Xy6yDT&%C81lPKpPXbqUhzzl{q3^Sk}HWnY7WJQe6jlGz!p0-7??J+bY zgA#37KIaHIKMAbm7zUqWS=e%u=&^Hxi8#Gn z{Tocq7m8wbDUEx%S-QPTHD^Hv&?#)%b#o-loVDvP9Bh=(F9$LybnTVPVyQ{%Wa>)a z6Ny1bwls;hn4>sUCzpq)GE&kNT3B=7?;>LO4lqNQWhFDUz54Er@~sv z0lFH>FX9<#nzAuEPO%s4nk^Zk&s+{5tqE+0XqWQ-sJP1(bVEW~W4gh(jZ?>NDMRm< zy!aif4sC3gcSfC0biX)Kky$fv+eL6?e=%ku0PbtZEk~fu#4y@En(?NJbVmMQnowH4 zKa0rsM9G9Ybnej(`Pu9rjj|_zvg_e$t7Kr7ByW}V)qmH89te*bY)4L!gZ+5I0zWLb zRYsZ^3y(ma;d+B|63&=L*#ocZ>PY#vtkezOF#%D z5fM!%?q>}0ix87O14KU)UI$*gKGl>Qn}PsxS?azJO8dRsR1ZPEBhaUE&WqzwTXvTCgGx?fMR|ah&5ruq8l8tM( z=OpUCehg$l>4I!5B%Ag(1YZQsZRc#nAN);#!mhdUa+;B@QU@mr_)>e?RMv%kVXjYJ z{3!P2jC}Lu>13AfxU=(aw9?Y%!z?_Vke<4)_9N-DK&z!3mwVuNwXk!O-zlh3W~Ey$ zt`Av0Gky^X?E*~^sQVmliy4C893SgL{OSA(-@*!1>kPLG0`N%Y)1@W3o&GseP(yDi zI*qvSie3WR_RbB0d}G0W#NVrd1z`ACXB>hSh~2)LlG7_|#^)~x-1!5d)BRxLEwUcu zMD|p@TZ2CS`-IXFyjW_lNnfVBS87HY``KRA3g*9sVz}W~3QCVEoDI7hzXNzb;K6+@ zz1;xH)D-#2{sb8qmJUQ;ClOil`&%h*O~b$gYV$^Y*SgOm)*60$=Q#zea0PQjN3~6^fi`Odskm z{!nQjyY^fP5E%{d+uBil@y#ge#g@LI*(ND%_>)Qput5b@i#f*(742ELnZJ>XEMnH> z;8oM;Wu~?tyA;9)n|?1POfP6v3?~x+RUl&2lCoU&%az3@84{#Vn1pmaAS_`Cr+7l( zaEq*^w^)>4pRkH>Cc0&K5$e(~sNC(|>h3{YWy;zhSz+ zrB|l^_wUcRr@GD1q@Do{{O>9ZA6Wt8a4@)j;3Xg#f7(*YM4(0^@emz-awkR|(Gy8%BFb{*ejRNGa~ z?th|lBDMrTM)As^wGhZ@u!*%f`J zPeG7T$}1;g)|PPpp*2Xy5M8X$*l{*r^7Il@++} zh}PWBMs4svve)yU8%rPo#zL1!71t?vnDm8+KTv=2 zltCIi`#l-#Yb4Kf;*(IycwNn}6(lC&67Cx}HV?2%1F#9q{7l_t5j&3NT6ec(@`hjh zlNB$UIO$q?j&`0U(%Vz~h@W@n(#U`{af}UqT7Dk(qd}{6;Cr`wyV-oY`hTlK;4K<0 z%ga5e_tlD(CcN&{;B}g;a?{Y+G=sn`>E1fTPh)2s=w(KQqzXAxYyl_+5XNo(iODCt z`{#Iljt_gl`K{?q!;T~HLwbzAkSsO3fSMbViI?ej`C$Nq#=7;NVW1Y}73VN_z9kFc zm@0aoVJ~9KPVihelbbOf;(T^HclIRM@r(K5`m>v~V1eW~3pS;R&=1Bpg81M0r1LXj zDOGV;>!B7IBFkFvFF%m2%_RM!xOq88A6ozX`}8w;@8djcbDN|?^~AC!o?`GHVmq;y zJteW6?Iv9qr>N_y3Q_MsIqOKpjhiEDV|hzT?0>DlKp>2f$6f%r2Y)f4qOIM<&9@aR zEoaigi3dYcVA^jSfpiM>Knx7A#GN0*+LRaB_c#?YQvT^aBDVitOR-oQZl6bO90Nee zIY$Fc{tDOv(_D#xIHUN~Gj=cu5Dz+2xM>bC#~RTtCP}V%Gd|cj9TifOWjqnw>VdRMqk~njvCZr^lWRZb7}X zD&Z?|YfS7E3ev|;gOUMZ;+6zy_&aY!yh*U^CNP36rF zH{gwuC_umjL^`|@?q57ZExiYTGBouo+D`nsbZX6k<{jd0PYmsXXVfFgmiOKV(({ zPTFxYN;QAUT25^1VqXukFVb^vEK@!i@PlO^n~lS{*VEqE6@>PcQrQpQvCZj?QYqJy zE3LcC$)*GNZ^ZLGD?JnT^J+nx5yi88jm1NugkNAUWPGh^oq2#t+MmB$Ksw>Zo!%JI znRpua5@yWAWTyFF&07b3L^^Y^o^{`FPHw#E7ZV_M7zQu~hi&~V>tlcmFUs)`(~1c# zq(qA~IfZQs+m@SWJXXGmIs}6RL;bgFCVj;$ZtAxs_8NNL#pG2KVd2Y;dj+VRrba-% z5eJA8^$HM6e8C$3Lj=48$W|C?>l;+pjA>DMqpeOG+da9tpb>5wBA8EQz5YQ#2hP;otcDzm1_(%*C$Qys=w(9>rLF@zR5*5@Py@Wq~>j zdmx>u5p+p^vVm`403(@h7!d8Cf1`LgyPS0kaMyW$DPbP#A{{uE*i+nX<^=y|F`PD# zT+uX0Y?rilW=Z@EBdvU6EVqXbx)0BYdkaP2TOtVFuVZs#|F8SvnKXzKL-9+yi@{Fi z0E3{u$Y4H=TS)rA`dA&@H2b(JWz9TG@^OU}*`O$pikTqCll9JQ+EbRSUPX%fuy89Rs^lKhU|JncbJ>({#N7qpH{l<#$oKKahQl4hC z&*aGoABom~ha3!%O32TXAN|smQxpYe;;b_h%ba8SRJeLu+@s6n#4(g0)ApY|RTM2~ zZ|H1#y9M2h#ilXPQV_iIGHpov_G3nx5UUPNntt{hw$*w|zW;h%)Su+aYi4|SRN7Xw zl4wn^kRvtPsFCtZFHRG=RHNS;Mk({|$~}77IHV&nFfB!PN2|=d=aS0u{OWN1VR&6{ zehMolp4l5}>;2#G3Mg9U128K5eQ9(;HsL71p%f`kUMgR(Y@Qz53*D>csFz{y>!YgxEk(wd;dH?;9eB0ON_0H)S7^-Hf8K}M zU_wg~`ShMv`hzBaQxw>X9cs<)3; c*S{jUdD>D;Ns8_cfOapC0!$SOmo^RhKkMk7LI3~& literal 0 HcmV?d00001 From 852dce2dffa051912fc208c71f5802f9322dbef3 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 8 Apr 2016 10:43:23 +1000 Subject: [PATCH 187/215] Add default logos and home_hero --- app/models/content_configuration.rb | 8 ++++---- spec/models/content_configuration_spec.rb | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 spec/models/content_configuration_spec.rb diff --git a/app/models/content_configuration.rb b/app/models/content_configuration.rb index 2357f8dfa3..e20279f5ee 100644 --- a/app/models/content_configuration.rb +++ b/app/models/content_configuration.rb @@ -7,14 +7,14 @@ class ContentConfiguration < Spree::Preferences::FileConfiguration preference :logo, :file preference :logo_mobile, :file preference :logo_mobile_svg, :file - has_attached_file :logo + has_attached_file :logo, default_url: "/assets/ofn-logo.png" has_attached_file :logo_mobile - has_attached_file :logo_mobile_svg + has_attached_file :logo_mobile_svg, default_url: "/assets/ofn-logo-mobile.svg" # Home page preference :home_hero, :file preference :home_show_stats, :boolean, default: true - has_attached_file :home_hero + has_attached_file :home_hero, default_url: "/assets/home/home.jpg" # Producer sign-up page preference :producer_signup_pricing_table_html, :text, default: "(TODO: Pricing table)" @@ -33,7 +33,7 @@ class ContentConfiguration < Spree::Preferences::FileConfiguration # Footer preference :footer_logo, :file - has_attached_file :footer_logo + has_attached_file :footer_logo, default_url: "/assets/ofn-logo-footer.png" preference :footer_facebook_url, :string, default: "https://www.facebook.com/OpenFoodNet" preference :footer_twitter_url, :string, default: "https://twitter.com/OpenFoodNet" preference :footer_instagram_url, :string, default: "" diff --git a/spec/models/content_configuration_spec.rb b/spec/models/content_configuration_spec.rb new file mode 100644 index 0000000000..6b7bc8a4e0 --- /dev/null +++ b/spec/models/content_configuration_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe ContentConfiguration do + describe "default logos and home_hero" do + it "sets a default url with existing image" do + expect(image_exist?(ContentConfig.logo.options[:default_url])).to be_true + expect(image_exist?(ContentConfig.logo_mobile_svg.options[:default_url])).to be_true + expect(image_exist?(ContentConfig.home_hero.options[:default_url])).to be_true + expect(image_exist?(ContentConfig.footer_logo.options[:default_url])).to be_true + end + + def image_exist?(default_url) + image_path = default_url.gsub(/\/assets\//,'/assets/images/') + File.exist?(File.join(Rails.root, 'app', image_path)) + end + end +end From 27fdfb60af61ca2fdd1ed7484df028502b894b69 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 9 Apr 2016 09:41:21 +0100 Subject: [PATCH 188/215] Override incorrect Spree GB translations for state/county --- config/locales/en-GB.yml | 5 +++++ db/schema.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 7301ef4f2c..d2fc055895 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -10,6 +10,11 @@ en-GB: password: confirmation: you have successfully registered too_short: pick a longer name + attributes: + spree/order: + payment_state: Payment State + shipment_state: Shipment State + devise: failure: invalid: | diff --git a/db/schema.rb b/db/schema.rb index c8887ad86e..dc54bd8f3d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -683,9 +683,9 @@ ActiveRecord::Schema.define(:version => 20160401043927) do t.string "email" t.text "special_instructions" t.integer "distributor_id" - t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" + t.integer "order_cycle_id" t.integer "cart_id" t.integer "customer_id" end From 39e3374ece47c6fdeed1dc3edbb0180a3a9de68f Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Mon, 11 Apr 2016 22:27:47 +0100 Subject: [PATCH 189/215] Add comment --- config/locales/en-GB.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index d2fc055895..931df49417 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -10,6 +10,7 @@ en-GB: password: confirmation: you have successfully registered too_short: pick a longer name + # Overridden here due to a bug in spree i18n (Issue #870) attributes: spree/order: payment_state: Payment State From eb846e27fd2398e7b23d227006fa0652837cecf3 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 13 Apr 2016 15:53:53 +1000 Subject: [PATCH 190/215] Run a job queue heartbeat every 5 minutes --- app/jobs/heartbeat_job.rb | 5 ++++ .../spree/app_configuration_decorator.rb | 3 +++ config/schedule.rb | 8 ++++++ spec/jobs/heartbeat_job_spec.rb | 27 +++++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 app/jobs/heartbeat_job.rb create mode 100644 spec/jobs/heartbeat_job_spec.rb diff --git a/app/jobs/heartbeat_job.rb b/app/jobs/heartbeat_job.rb new file mode 100644 index 0000000000..93e835905f --- /dev/null +++ b/app/jobs/heartbeat_job.rb @@ -0,0 +1,5 @@ +class HeartbeatJob + def perform + Spree::Config.last_job_queue_heartbeat_at = Time.now + end +end diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index fc7a8171cc..6ef1e7b848 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -20,4 +20,7 @@ Spree::AppConfiguration.class_eval do preference :account_invoices_monthly_rate, :decimal, default: 0 preference :account_invoices_monthly_cap, :decimal, default: 0 preference :account_invoices_tax_rate, :decimal, default: 0 + + # Monitoring + preference :last_job_queue_heartbeat_at, :string, default: nil end diff --git a/config/schedule.rb b/config/schedule.rb index 023382330a..6f8e7b582a 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -4,9 +4,13 @@ require 'whenever' env "MAILTO", "rohan@rohanmitchell.com" + # If we use -e with a file containing specs, rspec interprets it and filters out our examples job_type :run_file, "cd :path; :environment_variable=:environment bundle exec script/rails runner :task :output" +job_type :enqueue_job, "cd :path; :environment_variable=:environment bundle exec script/rails runner 'Delayed::Job.enqueue(:task.new, priority: :priority)' :output" + + every 1.hour do rake 'openfoodnetwork:cache:check_products_integrity' end @@ -23,6 +27,10 @@ every 4.hours do rake 'db2fog:backup' end +every 5.minutes do + enqueue_job 'QueueHeartbeatJob', priority: 0 +end + every 1.day, at: '1:00am' do rake 'openfoodnetwork:billing:update_account_invoices' end diff --git a/spec/jobs/heartbeat_job_spec.rb b/spec/jobs/heartbeat_job_spec.rb new file mode 100644 index 0000000000..3bafecd572 --- /dev/null +++ b/spec/jobs/heartbeat_job_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe HeartbeatJob do + context "with time frozen" do + let(:run_time) { Time.zone.local(2016, 4, 13, 13, 0, 0) } + + before { Spree::Config.last_job_queue_heartbeat_at = nil } + + around do |example| + Timecop.freeze(run_time) { example.run } + end + + it "updates the last_job_queue_heartbeat_at config var" do + run_job + Time.parse(Spree::Config.last_job_queue_heartbeat_at).should == run_time + end + end + + + private + + def run_job + clear_jobs + Delayed::Job.enqueue HeartbeatJob.new + flush_jobs ignore_exceptions: false + end +end From 193580d5d9e5ec60b389fbbb31d77a31fe93e16c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 13 Apr 2016 16:01:21 +1000 Subject: [PATCH 191/215] Enqueue jobs directly via SQL rather than loading full Rails stack --- config/schedule.rb | 5 ++-- script/enqueue | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100755 script/enqueue diff --git a/config/schedule.rb b/config/schedule.rb index 6f8e7b582a..8cc8d7d225 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -7,8 +7,7 @@ env "MAILTO", "rohan@rohanmitchell.com" # If we use -e with a file containing specs, rspec interprets it and filters out our examples job_type :run_file, "cd :path; :environment_variable=:environment bundle exec script/rails runner :task :output" - -job_type :enqueue_job, "cd :path; :environment_variable=:environment bundle exec script/rails runner 'Delayed::Job.enqueue(:task.new, priority: :priority)' :output" +job_type :enqueue_job, "cd :path; :environment_variable=:environment bundle exec script/enqueue :task :priority :output" every 1.hour do @@ -28,7 +27,7 @@ every 4.hours do end every 5.minutes do - enqueue_job 'QueueHeartbeatJob', priority: 0 + enqueue_job 'HeartbeatJob', priority: 0 end every 1.day, at: '1:00am' do diff --git a/script/enqueue b/script/enqueue new file mode 100755 index 0000000000..2071414e4e --- /dev/null +++ b/script/enqueue @@ -0,0 +1,61 @@ +#!/usr/bin/env ruby + +# Push a job onto the Delayed Job queue without booting the Rails stack +# Perfect for calling via cron. +# +# Use like this: +# +# ./script/enqueue +# ./script/enqueue Background::ImportJobs + +require 'erb' +require 'yaml' + +ENV["RAILS_ENV"] ||= "development" + +DATABASE_CONFIG = File.expand_path("../../config/database.yml", __FILE__) + +def psql + raise "Missing database.yml" unless File.exists?(DATABASE_CONFIG) + + file = File.read(DATABASE_CONFIG) + erb = ERB.new(file).result + env = ENV["RAILS_ENV"] + config = YAML.load(erb)[env] + + raise "Missing config for #{env} environment" unless config + + "psql".tap do |s| + s << " --host #{config['host']}" if config['host'] + s << " --user #{config['username']}" if config['username'] + s << " --port #{config['port']}" if config['port'] + s << " #{config['database']}" + end +end + +def enqueue_delayed_job(handler, priority=nil) + time = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S") + priority ||= 50 + + sql = <<-SQL + INSERT INTO delayed_jobs ( + handler, + created_at, + updated_at, + run_at, + priority + ) VALUES ( + '--- !ruby/object:#{handler} {}\n', + '#{time}', + '#{time}', + '#{time}', + #{priority} + ); + SQL + + IO.popen(psql, "w") do |io| + io.write sql + end +end + +enqueue_delayed_job ARGV[0], ARGV[1] From 59b564c4bebf9ec5924cb92385ac81d3038a2a5b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 13 Apr 2016 16:48:07 +1000 Subject: [PATCH 192/215] Report job queue status via API --- app/controllers/api/statuses_controller.rb | 17 +++++++++++ config/routes.rb | 4 +++ .../api/statuses_controller_spec.rb | 30 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 app/controllers/api/statuses_controller.rb create mode 100644 spec/controllers/api/statuses_controller_spec.rb diff --git a/app/controllers/api/statuses_controller.rb b/app/controllers/api/statuses_controller.rb new file mode 100644 index 0000000000..c8844b868b --- /dev/null +++ b/app/controllers/api/statuses_controller.rb @@ -0,0 +1,17 @@ +module Api + class StatusesController < BaseController + respond_to :json + + def job_queue + render json: {alive: job_queue_alive?} + end + + + private + + def job_queue_alive? + Spree::Config.last_job_queue_heartbeat_at.present? && + Time.parse(Spree::Config.last_job_queue_heartbeat_at) > 6.minutes.ago + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 26d675838c..6293117a3f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -143,6 +143,10 @@ Openfoodnetwork::Application.routes.draw do get :managed, on: :collection get :accessible, on: :collection end + + resource :status do + get :job_queue + end end namespace :open_food_network do diff --git a/spec/controllers/api/statuses_controller_spec.rb b/spec/controllers/api/statuses_controller_spec.rb new file mode 100644 index 0000000000..f2427efb79 --- /dev/null +++ b/spec/controllers/api/statuses_controller_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +module Api + describe StatusesController do + render_views + + describe "job queue status" do + it "returns alive when up to date" do + Spree::Config.last_job_queue_heartbeat_at = Time.now + spree_get :job_queue + response.should be_success + response.body.should == {alive: true}.to_json + end + + it "returns dead otherwise" do + Spree::Config.last_job_queue_heartbeat_at = 10.minutes.ago + spree_get :job_queue + response.should be_success + response.body.should == {alive: false}.to_json + end + + it "returns dead when no heartbeat recorded" do + Spree::Config.last_job_queue_heartbeat_at = nil + spree_get :job_queue + response.should be_success + response.body.should == {alive: false}.to_json + end + end + end +end From 0ac87b9e55164d9b266354b45008b435f68c48b6 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 13 Apr 2016 16:50:01 +1000 Subject: [PATCH 193/215] Override spree_i18n payment_state, shipment_state --- config/locales/en.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index e38d3e3f6b..4695e53754 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -15,6 +15,12 @@ # See http://community.openfoodnetwork.org/t/localisation-ofn-in-your-language/397 en: + activerecord: + # Overridden here due to a bug in spree i18n (Issue #870) + attributes: + spree/order: + payment_state: Payment State + shipment_state: Shipment State devise: failure: invalid: | From a2c58c7ca18163d43944a7092a449f7a0329bcc4 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 13 Apr 2016 16:59:54 +1000 Subject: [PATCH 194/215] Match array without order in ocm spec That spec was randomly failing. --- .../lib/open_food_network/order_cycle_management_report_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/open_food_network/order_cycle_management_report_spec.rb b/spec/lib/open_food_network/order_cycle_management_report_spec.rb index 2ac752561e..77b9dc3f8e 100644 --- a/spec/lib/open_food_network/order_cycle_management_report_spec.rb +++ b/spec/lib/open_food_network/order_cycle_management_report_spec.rb @@ -102,7 +102,7 @@ module OpenFoodNetwork order3 = create(:order, shipping_method: sm3) subject.stub(:params).and_return(shipping_method_in: [sm1.id, sm3.id]) - subject.filter(orders).should == [order1, order3] + expect(subject.filter(orders)).to match_array [order1, order3] end it "should do all the filters at once" do From 53e6d391e3fdbb6c76e1ededebb9ca4eea8204c4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 14 Apr 2016 09:40:55 +1000 Subject: [PATCH 195/215] Fix intermittent fail on insignificant result ordering --- .../open_food_network/order_cycle_management_report_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/open_food_network/order_cycle_management_report_spec.rb b/spec/lib/open_food_network/order_cycle_management_report_spec.rb index 2ac752561e..063470f37a 100644 --- a/spec/lib/open_food_network/order_cycle_management_report_spec.rb +++ b/spec/lib/open_food_network/order_cycle_management_report_spec.rb @@ -92,7 +92,7 @@ module OpenFoodNetwork # payment2 = create(:payment, order: order2, payment_method: pm2) subject.stub(:params).and_return(payment_method_in: [pm1.id, pm3.id] ) - subject.filter(orders).should == [order1, order3] + subject.filter(orders).should match_array [order1, order3] end it "filters to a shipping method" do @@ -102,7 +102,7 @@ module OpenFoodNetwork order3 = create(:order, shipping_method: sm3) subject.stub(:params).and_return(shipping_method_in: [sm1.id, sm3.id]) - subject.filter(orders).should == [order1, order3] + subject.filter(orders).should match_array [order1, order3] end it "should do all the filters at once" do From f15e9480854cfc6ad0d9f7833576e57aeec6c4df Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Thu, 14 Apr 2016 15:26:55 -0500 Subject: [PATCH 196/215] small i18n fixes #878 --- .../add_accounts_and_billing.html.haml.deface | 2 +- .../accounts_and_billing_settings/edit.html.haml | 4 ++-- app/views/spree/admin/reports/customers.html.haml | 2 +- .../admin/reports/order_cycle_management.html.haml | 2 +- .../reports/orders_and_distributors.html.haml | 3 +-- .../admin/shared/_trial_progress_bar.html.haml | 4 ++-- config/locales/en.yml | 14 +++++--------- 7 files changed, 13 insertions(+), 18 deletions(-) diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface index 14f4925206..37754bb40e 100644 --- a/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface +++ b/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface @@ -1,4 +1,4 @@ // insert_bottom "[data-hook='admin_configurations_sidebar_menu']" %li - = link_to 'Accounts & Billing', main_app.edit_admin_accounts_and_billing_settings_path + = link_to t(:accounts_and_billing), main_app.edit_admin_accounts_and_billing_settings_path diff --git a/app/views/admin/accounts_and_billing_settings/edit.html.haml b/app/views/admin/accounts_and_billing_settings/edit.html.haml index 2121719e75..72ce7920a9 100644 --- a/app/views/admin/accounts_and_billing_settings/edit.html.haml +++ b/app/views/admin/accounts_and_billing_settings/edit.html.haml @@ -1,7 +1,7 @@ = render :partial => 'spree/admin/shared/configuration_menu' - content_for :page_title do - = t(:accounts_and_billing_settings) + = t(:accounts_and_billing) = render 'spree/shared/error_messages', target: @settings @@ -11,7 +11,7 @@ %legend =t :admin_settings = form_for @settings, as: :settings, url: main_app.admin_accounts_and_billing_settings_path, :method => :put do |f| - .row{ ng: { app: t(:admin_accounts_and_billing) } } + .row{ ng: { app: 'admin.accounts_and_billing_settings' } } .twelve.columns.alpha.omega .field = f.label :accounts_distributor_id, t(:accounts_administration_distributor) diff --git a/app/views/spree/admin/reports/customers.html.haml b/app/views/spree/admin/reports/customers.html.haml index 70045662f9..3ab25e5c6c 100644 --- a/app/views/spree/admin/reports/customers.html.haml +++ b/app/views/spree/admin/reports/customers.html.haml @@ -33,7 +33,7 @@ %br %table#listing_customers.index %thead - %tr{'data-hook' => "orders_header" } + %tr{'data-hook' => "orders_header"} - @report.header.each do |heading| %th=heading %tbody 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 0b32eabe78..808c1ead61 100644 --- a/app/views/spree/admin/reports/order_cycle_management.html.haml +++ b/app/views/spree/admin/reports/order_cycle_management.html.haml @@ -33,7 +33,7 @@ %br %table#listing_ocm_orders.index %thead - %tr{'data-hook' => "orders_header" } + %tr{'data-hook' => "orders_header"} - @report.header.each do |heading| %th=heading %tbody diff --git a/app/views/spree/admin/reports/orders_and_distributors.html.haml b/app/views/spree/admin/reports/orders_and_distributors.html.haml index 9d733cc995..aa710b1db6 100644 --- a/app/views/spree/admin/reports/orders_and_distributors.html.haml +++ b/app/views/spree/admin/reports/orders_and_distributors.html.haml @@ -10,7 +10,7 @@ %br %table#listing_orders.index %thead - %tr{'data-hook' => t(:report_customers_header)} + %tr{'data-hook' => 'orders_header'} - @report.header.each do |heading| %th=heading %tbody @@ -21,4 +21,3 @@ - if @report.table.empty? %tr %td{:colspan => "2"}= t(:none) - diff --git a/app/views/spree/admin/shared/_trial_progress_bar.html.haml b/app/views/spree/admin/shared/_trial_progress_bar.html.haml index 999dd9a13f..3deef097fe 100644 --- a/app/views/spree/admin/shared/_trial_progress_bar.html.haml +++ b/app/views/spree/admin/shared/_trial_progress_bar.html.haml @@ -1,7 +1,7 @@ - if enterprise -if shop_trial_in_progress?(enterprise) #trial_progress_bar - = t(:shop_trial_in_progress) + = t(:shop_trial_in_progress, days: remaining_trial_days(enterprise)) -elsif shop_trial_expired?(enterprise) #trial_progress_bar - = t(:shop_trial_expired) \ No newline at end of file + = t(:shop_trial_expired) diff --git a/config/locales/en.yml b/config/locales/en.yml index e38d3e3f6b..ed1fd410db 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -772,7 +772,7 @@ Please follow the instructions there to make your enterprise visible on the Open shop_variant_quantity_max: "max" contact: "Contact" follow: "Follow" - shop_for_products_html: "Shop for %{enterprise} products at:" #FIXME + shop_for_products_html: "Shop for %{enterprise} products at:" change_shop: "Change shop to:" shop_at: "Shop now at:" price_breakdown: "Full price breakdown" @@ -864,13 +864,12 @@ Please follow the instructions there to make your enterprise visible on the Open go: "Go" hub: "Hub" accounts_administration_distributor: "accounts administration distributor" - admin_accounts_and_billing: "admin.accounts_and_billing_settings" #FIXME + accounts_and_billing: "Accounts & Billing" producer: "Producer" product: "Product" price: "Price" on_hand: "On hand" save_changes: "Save Changes" - update_action: "update()" #FIXME spree_admin_overview_enterprises_header: "My Enterprises" spree_admin_overview_enterprises_footer: "MANAGE MY ENTERPRISES" spree_admin_enterprises_hubs_name: "Name" @@ -906,8 +905,8 @@ Please follow the instructions there to make your enterprise visible on the Open live: "live" manage: "Manage" resend: "Resend" - add_and_manage_products: "Add & manage products" - add_and_manage_order_cycles: "Add & manage order cycles" + add_and_manage_products: "Add & manage products" + add_and_manage_order_cycles: "Add & manage order cycles" manage_order_cycles: "Manage order cycles" manage_products: "Manage products" edit_profile_details: "Edit profile details" @@ -940,14 +939,13 @@ Please follow the instructions there to make your enterprise visible on the Open hub_sidebar_at_least: "At least one hub must be selected" hub_sidebar_blue: "blue" hub_sidebar_red: "red" - shop_trial_in_progress: "Your shopfront trial expires in #{remaining_trial_days(enterprise)}." #FIXME + shop_trial_in_progress: "Your shopfront trial expires in %{days}." shop_trial_expired: "Good news! We have decided to extend shopfront trials until further notice (probably around March 2015)." #FIXME report_customers_distributor: "Distributor" report_customers_supplier: "Supplier" report_customers_cycle: "Order Cycle" report_customers_type: "Report Type" report_customers_csv: "Download as csv" - report_customers_header: "orders header" report_producers: "Producers: " report_type: "Report Type: " report_hubs: "Hubs: " @@ -958,8 +956,6 @@ Please follow the instructions there to make your enterprise visible on the Open report_payment_totals: 'Payment Totals' report_all: 'all' report_order_cycle: "Order Cycle: " - report_product_header: "products_header" - report_order_header: "orders_header" report_entreprises: "Enterprises: " report_users: "Users: " initial_invoice_number: "Initial invoice number:" From 9e4610f9bd2808d06b642ec7482c6752f95ec12b Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 8 Apr 2016 09:33:09 +1000 Subject: [PATCH 197/215] Customers: enable change of hub if no customers Fix #588. And show search bar even when no customers are shown. Previously, the search bar disappeared when you entered a search without results. --- .../customers/controllers/customers_controller.js.coffee | 6 +++--- app/views/admin/customers/index.html.haml | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee index c475f1e4df..8ebe07d456 100644 --- a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee +++ b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee @@ -1,5 +1,5 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, Customers, Columns, pendingChanges, shops) -> - $scope.shop = null + $scope.shop = {} $scope.shops = shops $scope.submitAll = pendingChanges.submitAll @@ -8,8 +8,8 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, Customers code: { name: "Code", visible: true } tags: { name: "Tags", visible: true } - $scope.$watch "shop", -> - if $scope.shop? + $scope.$watch "shop.id", -> + if $scope.shop.id? Customers.loaded = false $scope.customers = Customers.index(enterprise_id: $scope.shop.id) diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index f68ad37da1..1b2dc11547 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -5,7 +5,7 @@ = admin_inject_shops %div{ ng: { app: 'admin.customers', controller: 'customersCtrl' } } - .row{ ng: { hide: "loaded() && filteredCustomers.length > 0" } } + .row{ ng: { hide: "loaded() && customers.length > 0" } } .five.columns.alpha %h3 =t :please_select_hub @@ -13,15 +13,14 @@ %select.select2.fullwidth#shop_id{ 'ng-model' => 'shop.id', name: 'shop_id', 'ng-options' => 'shop.id as shop.name for shop in shops' } .seven.columns.omega   - .row{ 'ng-hide' => '!loaded() || filteredCustomers.length == 0' } + .row{ 'ng-hide' => '!loaded() || customers.length == 0' } .controls.sixteen.columns.alpha.omega .five.columns.alpha %input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } .five.columns   - -# =render 'admin/shared/bulk_actions_dropdown' .three.columns   = render 'admin/shared/columns_dropdown' - .row{ 'ng-if' => 'shop && !loaded()' } + .row{ 'ng-if' => 'shop.id && !loaded()' } .sixteen.columns.alpha#loading %img.spinner{ src: "/assets/spinning-circles.svg" } %h1 From ecd32819a6b056a815472f5adac716a3889b16ea Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 13 Apr 2016 14:47:11 +1000 Subject: [PATCH 198/215] Remove unused matcher --- .../inventory_items/services/inventory_items_spec.js.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/javascripts/unit/admin/inventory_items/services/inventory_items_spec.js.coffee b/spec/javascripts/unit/admin/inventory_items/services/inventory_items_spec.js.coffee index 49ea827900..61d002f23a 100644 --- a/spec/javascripts/unit/admin/inventory_items/services/inventory_items_spec.js.coffee +++ b/spec/javascripts/unit/admin/inventory_items/services/inventory_items_spec.js.coffee @@ -8,10 +8,6 @@ describe "InventoryItems service", -> $provide.value 'inventoryItems', inventoryItems null - this.addMatchers - toDeepEqual: (expected) -> - return angular.equals(this.actual, expected) - inject ($q, _$httpBackend_, _InventoryItems_, _InventoryItemResource_) -> InventoryItems = _InventoryItems_ InventoryItemResource = _InventoryItemResource_ From 831df0b222e2ff5f25ae870028cf1d2271cd719c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 13 Apr 2016 15:05:39 +1000 Subject: [PATCH 199/215] Create and delete customers in admin interface Removed Customers service and extended CustomerResource. --- .../customers_controller.js.coffee | 26 ++++++++-- .../services/customer_resource.js.coffee | 11 ++++- .../customers/services/customers.js.coffee | 16 ------- app/controllers/admin/customers_controller.rb | 10 ++++ app/models/spree/ability_decorator.rb | 5 ++ app/views/admin/customers/index.html.haml | 21 +++++--- config/locales/en.yml | 4 ++ config/routes.rb | 2 +- .../admin/customers_controller_spec.rb | 33 +++++++++++++ .../customers_controller_spec.js.coffee | 48 ++++++++++++++----- .../services/customers_spec.js.coffee | 31 ------------ 11 files changed, 134 insertions(+), 73 deletions(-) delete mode 100644 app/assets/javascripts/admin/customers/services/customers.js.coffee delete mode 100644 spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee diff --git a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee index 8ebe07d456..d2e6d58562 100644 --- a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee +++ b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.customers").controller "customersCtrl", ($scope, Customers, Columns, pendingChanges, shops) -> +angular.module("admin.customers").controller "customersCtrl", ($scope, CustomerResource, Columns, pendingChanges, shops) -> $scope.shop = {} $scope.shops = shops $scope.submitAll = pendingChanges.submitAll @@ -10,8 +10,24 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, Customers $scope.$watch "shop.id", -> if $scope.shop.id? - Customers.loaded = false - $scope.customers = Customers.index(enterprise_id: $scope.shop.id) + $scope.customers = index {enterprise_id: $scope.shop.id} - $scope.loaded = -> - Customers.loaded + $scope.add = (email) -> + params = + enterprise_id: $scope.shop.id + email: email + CustomerResource.create params, (customer) => + if customer.id + $scope.customers.push customer + $scope.quickSearch = customer.email + + $scope.deleteCustomer = (customer) -> + params = id: customer.id + CustomerResource.destroy params, -> + i = $scope.customers.indexOf customer + $scope.customers.splice i, 1 unless i < 0 + + index = (params) -> + $scope.loaded = false + CustomerResource.index params, => + $scope.loaded = true diff --git a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee b/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee index 523e0c1495..5b6c1ab205 100644 --- a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee +++ b/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee @@ -1,8 +1,17 @@ angular.module("admin.customers").factory 'CustomerResource', ($resource) -> - $resource('/admin/customers.json', {}, { + $resource('/admin/customers/:id.json', {}, { 'index': method: 'GET' isArray: true params: enterprise_id: '@enterprise_id' + 'create': + method: 'POST' + params: + enterprise_id: '@enterprise_id' + email: '@email' + 'destroy': + method: 'DELETE' + params: + id: '@id' }) diff --git a/app/assets/javascripts/admin/customers/services/customers.js.coffee b/app/assets/javascripts/admin/customers/services/customers.js.coffee deleted file mode 100644 index 9acfa317d2..0000000000 --- a/app/assets/javascripts/admin/customers/services/customers.js.coffee +++ /dev/null @@ -1,16 +0,0 @@ -angular.module("admin.customers").factory 'Customers', (CustomerResource) -> - new class Customers - customers: [] - customers_by_id: {} - loaded: false - - index: (params={}, callback=null) -> - CustomerResource.index params, (data) => - for customer in data - @customers.push customer - @customers_by_id[customer.id] = customer - - @loaded = true - (callback || angular.noop)(@customers) - - @customers diff --git a/app/controllers/admin/customers_controller.rb b/app/controllers/admin/customers_controller.rb index b1ceb88c2f..14712662b6 100644 --- a/app/controllers/admin/customers_controller.rb +++ b/app/controllers/admin/customers_controller.rb @@ -14,6 +14,16 @@ module Admin end end + def create + @customer = Customer.new(params[:customer]) + if spree_current_user.enterprises.include? @customer.enterprise + @customer.save + render json: Api::Admin::CustomerSerializer.new(@customer).to_json + else + redirect_to '/unauthorized' + end + end + private def collection diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index c234cf1358..867d3c9e2b 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -101,6 +101,11 @@ class AbilityDecorator can [:print], Spree::Order do |order| order.user == user end + + can [:create], Customer + can [:destroy], Customer do |customer| + user.enterprises.include? customer.enterprise + end end def add_product_management_abilities(user) diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index 1b2dc11547..e33b871df4 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -5,7 +5,7 @@ = admin_inject_shops %div{ ng: { app: 'admin.customers', controller: 'customersCtrl' } } - .row{ ng: { hide: "loaded() && customers.length > 0" } } + .row{ ng: { hide: "loaded && customers.length > 0" } } .five.columns.alpha %h3 =t :please_select_hub @@ -13,24 +13,23 @@ %select.select2.fullwidth#shop_id{ 'ng-model' => 'shop.id', name: 'shop_id', 'ng-options' => 'shop.id as shop.name for shop in shops' } .seven.columns.omega   - .row{ 'ng-hide' => '!loaded() || customers.length == 0' } + .row{ 'ng-hide' => '!loaded || customers.length == 0' } .controls.sixteen.columns.alpha.omega .five.columns.alpha %input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } - .five.columns   - .three.columns   + .eight.columns   = render 'admin/shared/columns_dropdown' - .row{ 'ng-if' => 'shop.id && !loaded()' } + .row{ 'ng-if' => 'shop.id && !loaded' } .sixteen.columns.alpha#loading %img.spinner{ src: "/assets/spinning-circles.svg" } %h1 =t :loading_customers - .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded() && filteredCustomers.length == 0'} + .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded && filteredCustomers.length == 0'} %h1#no_results =t :no_customers_found - .row{ ng: { show: "loaded() && filteredCustomers.length > 0" } } + .row{ ng: { show: "loaded && filteredCustomers.length > 0" } } %form{ name: "customers" } %table.index#customers %col.email{ width: "20%"} @@ -61,3 +60,11 @@ %td.actions %a{ 'ng-click' => "deleteCustomer(customer)", :class => "delete-customer icon-trash no-text" } %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'submitAll()' } + + %form{ng: {show: "loaded", submit: 'add(newCustomerEmail)'}} + %h2= t '.add_new_customer' + .row + .five.columns.alpha + %input.fullwidth{type: "text", placeholder: t('.customer_placeholder'), ng: {model: 'newCustomerEmail'}} + .eleven.columns.omega + %input{type: "submit", value: t('.add_customer')} diff --git a/config/locales/en.yml b/config/locales/en.yml index 40926259f7..7b5f9b168f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -80,6 +80,10 @@ en: whats_this: What's this? + customers: + index: + add_customer: "Add customer" + customer_placeholder: "customer@example.org" inventory: title: Inventory description: Use this page to manage inventories for your enterprises. Any product details set here will override those set on the 'Products' page diff --git a/config/routes.rb b/config/routes.rb index 6293117a3f..8d8d6d27c3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -115,7 +115,7 @@ Openfoodnetwork::Application.routes.draw do resources :inventory_items, only: [:create, :update] - resources :customers, only: [:index, :update] + resources :customers, only: [:index, :create, :update, :destroy] resource :content diff --git a/spec/controllers/admin/customers_controller_spec.rb b/spec/controllers/admin/customers_controller_spec.rb index bb2e4888c2..2f9b2024e9 100644 --- a/spec/controllers/admin/customers_controller_spec.rb +++ b/spec/controllers/admin/customers_controller_spec.rb @@ -94,4 +94,37 @@ describe Admin::CustomersController, type: :controller do end end end + + describe "create" do + let(:enterprise) { create(:distributor_enterprise) } + let(:another_enterprise) { create(:distributor_enterprise) } + + def create_customer(enterprise) + spree_put :create, format: :json, customer: { email: 'new@example.com', enterprise_id: enterprise.id } + end + + context "json" do + let!(:customer) { create(:customer, enterprise: enterprise) } + + context "where I manage the customer's enterprise" do + before do + controller.stub spree_current_user: enterprise.owner + end + + it "allows me to create the customer" do + expect { create_customer enterprise }.to change(Customer, :count).by(1) + end + end + + context "where I don't manage the customer's enterprise" do + before do + controller.stub spree_current_user: another_enterprise.owner + end + + it "prevents me from creating the customer" do + expect { create_customer enterprise }.to change(Customer, :count).by(0) + end + end + end + end end diff --git a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee index 22777a6528..215f2834ed 100644 --- a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee @@ -1,25 +1,49 @@ describe "CustomersCtrl", -> - ctrl = null scope = null - Customers = null + http = null beforeEach -> - shops = "list of shops" - module('admin.customers') - inject ($controller, $rootScope, _Customers_) -> + inject ($controller, $rootScope, _CustomerResource_, $httpBackend) -> scope = $rootScope - Customers = _Customers_ - ctrl = $controller 'customersCtrl', {$scope: scope, Customers: Customers, shops: shops} + http = $httpBackend + $controller 'customersCtrl', {$scope: scope, CustomerResource: _CustomerResource_, shops: {}} + this.addMatchers + toAngularEqual: (expected) -> + return angular.equals(this.actual, expected) + + it "has no shop pre-selected", -> + expect(scope.shop).toEqual {} describe "setting the shop on scope", -> + customer = { id: 5, email: 'someone@email.com'} + customers = [customer] + beforeEach -> - spyOn(Customers, "index").andReturn "list of customers" + http.expectGET('/admin/customers.json?enterprise_id=1').respond 200, customers scope.$apply -> scope.shop = {id: 1} + http.flush() - it "calls Customers#index with the correct params", -> - expect(Customers.index).toHaveBeenCalledWith({enterprise_id: 1}) + it "retrievs the list of customers", -> + expect(scope.customers).toAngularEqual customers - it "resets $scope.customers with the result of Customers#index", -> - expect(scope.customers).toEqual "list of customers" + describe "scope.add", -> + it "creates a new customer", -> + email = "customer@example.org" + newCustomer = {id: 6, email: email} + customers.push(newCustomer) + http.expectPOST('/admin/customers.json?email=' + email + '&enterprise_id=1').respond 200, newCustomer + scope.add(email) + http.flush() + expect(scope.customers).toAngularEqual customers + + describe "scope.deleteCustomer", -> + it "deletes a customer", -> + expect(scope.customers.length).toBe 2 + customer = scope.customers[0] + http.expectDELETE('/admin/customers/' + customer.id + '.json').respond 200 + scope.deleteCustomer(customer) + http.flush() + expect(scope.customers.length).toBe 1 + expect(scope.customers[0]).not.toAngularEqual customer diff --git a/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee b/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee deleted file mode 100644 index 7123055d63..0000000000 --- a/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee +++ /dev/null @@ -1,31 +0,0 @@ -describe "Customers service", -> - Customers = CustomerResource = customers = $httpBackend = null - - beforeEach -> - module 'admin.customers' - - inject ($q, _$httpBackend_, _Customers_, _CustomerResource_) -> - Customers = _Customers_ - CustomerResource = _CustomerResource_ - $httpBackend = _$httpBackend_ - $httpBackend.expectGET('/admin/customers.json?enterprise_id=2').respond 200, [{ id: 5, email: 'someone@email.com'}] - - describe "#index", -> - result = null - - beforeEach -> - expect(Customers.loaded).toBe false - result = Customers.index(enterprise_id: 2) - $httpBackend.flush() - - it "stores returned data in @customers, with ids as keys", -> - # This is super weird and freaking annoying. I think resource results have extra - # properties ($then, $promise) that cause them to not be equal to the reponse object - # provided to the expectGET clause above. - expect(Customers.customers).toEqual [ new CustomerResource({ id: 5, email: 'someone@email.com'}) ] - - it "returns @customers", -> - expect(result).toEqual Customers.customers - - it "sets @loaded to true", -> - expect(Customers.loaded).toBe true From a25f4fdf44cb218343a2df3ae94748be892b24bc Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 15 Apr 2016 11:57:18 +1000 Subject: [PATCH 200/215] Associate new users with existing customer records --- app/models/spree/user_decorator.rb | 5 ++++ spec/models/spree/user_spec.rb | 43 +++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 17473fd521..be60307d52 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -15,6 +15,7 @@ Spree.user_class.class_eval do accepts_nested_attributes_for :enterprise_roles, :allow_destroy => true attr_accessible :enterprise_ids, :enterprise_roles_attributes, :enterprise_limit + after_create :associate_customers after_create :send_signup_confirmation validate :limit_owned_enterprises @@ -41,6 +42,10 @@ Spree.user_class.class_eval do customers.of(enterprise).first end + def associate_customers + Customer.update_all({ user_id: id }, { user_id: nil, email: email }) + end + def send_signup_confirmation Delayed::Job.enqueue ConfirmSignupJob.new(id) end diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index ee01326316..923bc93183 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -17,11 +17,11 @@ describe Spree.user_class do it "enforces the limit on the number of enterprise owned" do expect(u2.owned_enterprises(:reload)).to eq [] u2.owned_enterprises << e1 - expect(u2.save!).to_not raise_error - expect { + expect { u2.save! }.to_not raise_error + expect do u2.owned_enterprises << e2 u2.save! - }.to raise_error ActiveRecord::RecordInvalid, "Validation failed: #{u2.email} is not permitted to own any more enterprises (limit is 1)." + end.to raise_error ActiveRecord::RecordInvalid, "Validation failed: #{u2.email} is not permitted to own any more enterprises (limit is 1)." end end @@ -53,6 +53,23 @@ describe Spree.user_class do create(:user) end.to enqueue_job ConfirmSignupJob end + + it "should not create a customer" do + expect do + create(:user) + end.to change(Customer, :count).by(0) + end + + describe "when a customer record exists" do + let!(:customer) { create(:customer, user: nil) } + + it "should not create a customer" do + expect(customer.user).to be nil + user = create(:user, email: customer.email) + customer.reload + expect(customer.user).to eq user + end + end end describe "known_users" do @@ -65,9 +82,9 @@ describe Spree.user_class do it "returns a list of users which manage shared enterprises" do expect(u1.known_users).to include u1, u2 expect(u1.known_users).to_not include u3 - expect(u2.known_users).to include u1,u2 + expect(u2.known_users).to include u1, u2 expect(u2.known_users).to_not include u3 - expect(u3.known_users).to_not include u1,u2,u3 + expect(u3.known_users).to_not include u1, u2, u3 end end @@ -85,14 +102,14 @@ describe Spree.user_class do let!(:u2) { create(:user) } let!(:distributor1) { create(:distributor_enterprise) } let!(:distributor2) { create(:distributor_enterprise) } - let!(:d1o1) { create(:completed_order_with_totals, distributor: distributor1, user_id: u1.id)} - let!(:d1o2) { create(:completed_order_with_totals, distributor: distributor1, user_id: u1.id)} - let!(:d1_order_for_u2) { create(:completed_order_with_totals, distributor: distributor1, user_id: u2.id)} - let!(:d1o3) { create(:order, state: 'cart', distributor: distributor1, user_id: u1.id)} - let!(:d2o1) { create(:completed_order_with_totals, distributor: distributor2, user_id: u2.id)} + let!(:d1o1) { create(:completed_order_with_totals, distributor: distributor1, user_id: u1.id) } + let!(:d1o2) { create(:completed_order_with_totals, distributor: distributor1, user_id: u1.id) } + let!(:d1_order_for_u2) { create(:completed_order_with_totals, distributor: distributor1, user_id: u2.id) } + let!(:d1o3) { create(:order, state: 'cart', distributor: distributor1, user_id: u1.id) } + let!(:d2o1) { create(:completed_order_with_totals, distributor: distributor2, user_id: u2.id) } - let!(:completed_payment) { create(:payment, order: d1o1, state: 'completed')} - let!(:payment) { create(:payment, order: d1o2, state: 'invalid')} + let!(:completed_payment) { create(:payment, order: d1o1, state: 'completed') } + let!(:payment) { create(:payment, order: d1o2, state: 'invalid') } it "returns enterprises that the user has ordered from" do expect(u1.enterprises_ordered_from).to eq [distributor1.id] @@ -115,7 +132,7 @@ describe Spree.user_class do end it "doesn't return uncompleted payments" do - expect(u1.orders_by_distributor.first.distributed_orders.map{|o| o.payments}.flatten).not_to include payment + expect(u1.orders_by_distributor.first.distributed_orders.map(&:payments).flatten).not_to include payment end end end From e17b6095173f7bd5311f9dbcccfb5238efa0c91d Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 15 Apr 2016 14:31:54 +1000 Subject: [PATCH 201/215] Apply code standards --- app/controllers/admin/customers_controller.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/customers_controller.rb b/app/controllers/admin/customers_controller.rb index 14712662b6..0b38b9c7de 100644 --- a/app/controllers/admin/customers_controller.rb +++ b/app/controllers/admin/customers_controller.rb @@ -7,9 +7,11 @@ module Admin respond_to do |format| format.html format.json do - render json: ActiveModel::ArraySerializer.new( @collection, - each_serializer: Api::Admin::CustomerSerializer, spree_current_user: spree_current_user - ).to_json + serialised = ActiveModel::ArraySerializer.new( + @collection, + each_serializer: Api::Admin::CustomerSerializer, + spree_current_user: spree_current_user) + render json: serialised.to_json end end end From 48f1794d7018500791f8cd347635f6c943fb3dc0 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 15 Apr 2016 14:34:29 +1000 Subject: [PATCH 202/215] Allow admin user to create customers --- app/controllers/admin/customers_controller.rb | 7 ++++++- spec/controllers/admin/customers_controller_spec.rb | 12 ++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/customers_controller.rb b/app/controllers/admin/customers_controller.rb index 0b38b9c7de..bd865c8130 100644 --- a/app/controllers/admin/customers_controller.rb +++ b/app/controllers/admin/customers_controller.rb @@ -18,7 +18,7 @@ module Admin def create @customer = Customer.new(params[:customer]) - if spree_current_user.enterprises.include? @customer.enterprise + if user_can_create_customer? @customer.save render json: Api::Admin::CustomerSerializer.new(@customer).to_json else @@ -37,5 +37,10 @@ module Admin def load_managed_shops @shops = Enterprise.managed_by(spree_current_user).is_distributor end + + def user_can_create_customer? + spree_current_user.admin? || + spree_current_user.enterprises.include?(@customer.enterprise) + end end end diff --git a/spec/controllers/admin/customers_controller_spec.rb b/spec/controllers/admin/customers_controller_spec.rb index 2f9b2024e9..f64a8057e8 100644 --- a/spec/controllers/admin/customers_controller_spec.rb +++ b/spec/controllers/admin/customers_controller_spec.rb @@ -104,8 +104,6 @@ describe Admin::CustomersController, type: :controller do end context "json" do - let!(:customer) { create(:customer, enterprise: enterprise) } - context "where I manage the customer's enterprise" do before do controller.stub spree_current_user: enterprise.owner @@ -125,6 +123,16 @@ describe Admin::CustomersController, type: :controller do expect { create_customer enterprise }.to change(Customer, :count).by(0) end end + + context "where I am the admin user" do + before do + controller.stub spree_current_user: create(:admin_user) + end + + it "allows admins to create the customer" do + expect { create_customer enterprise }.to change(Customer, :count).by(1) + end + end end end end From 49febc63338207cca05b4ca02fa7afed2263ec40 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 15 Apr 2016 15:35:43 +1000 Subject: [PATCH 203/215] Store customer email as lower case --- app/models/customer.rb | 6 ++++++ app/models/spree/order_decorator.rb | 4 ++-- spec/models/customer_spec.rb | 22 ++++++++++++++++------ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index bcafd3246b..34f62a6aa6 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -4,6 +4,8 @@ class Customer < ActiveRecord::Base belongs_to :enterprise belongs_to :user, class_name: Spree.user_class + before_validation :downcase_email + validates :code, uniqueness: { scope: :enterprise_id, allow_blank: true, allow_nil: true } validates :email, presence: true, uniqueness: { scope: :enterprise_id, message: I18n.t('validation_msg_is_associated_with_an_exising_customer') } validates :enterprise_id, presence: true @@ -14,6 +16,10 @@ class Customer < ActiveRecord::Base private + def downcase_email + email.andand.downcase! + end + def associate_user self.user = user || Spree::User.find_by_email(email) end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 8e3c6b7bb4..f0e9324185 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -284,11 +284,11 @@ Spree::Order.class_eval do def customer_is_valid? return true unless require_customer? - customer.present? && customer.enterprise_id == distributor_id && customer.email == (user.andand.email || email) + customer.present? && customer.enterprise_id == distributor_id && customer.email == email_for_customer end def email_for_customer - user.andand.email || email + (user.andand.email || email).andand.downcase end def associate_customer diff --git a/spec/models/customer_spec.rb b/spec/models/customer_spec.rb index 0e43ce9df5..f2ac14e9b2 100644 --- a/spec/models/customer_spec.rb +++ b/spec/models/customer_spec.rb @@ -6,15 +6,25 @@ describe Customer, type: :model do let!(:user2) { create(:user) } let!(:enterprise) { create(:distributor_enterprise) } + it "associates no user using non-existing email" do + c = Customer.create(enterprise: enterprise, email: 'some-email-not-associated-with-a-user@email.com') + expect(c.user).to be_nil + end + it "associates an existing user using email" do - c1 = Customer.create(enterprise: enterprise, email: 'some-email-not-associated-with-a-user@email.com') - expect(c1.user).to be_nil + non_existing_email = 'some-email-not-associated-with-a-user@email.com' + c1 = Customer.create(enterprise: enterprise, email: non_existing_email, user: user1) + expect(c1.user).to eq user1 + expect(c1.email).to eq non_existing_email + expect(c1.email).to_not eq user1.email - c2 = Customer.create(enterprise: enterprise, email: 'some-email-not-associated-with-a-user@email.com', user: user1) - expect(c2.user).to eq user1 + c2 = Customer.create(enterprise: enterprise, email: user2.email) + expect(c2.user).to eq user2 + end - c3 = Customer.create(enterprise: enterprise, email: user2.email) - expect(c3.user).to eq user2 + it "associates an existing user using email case-insensitive" do + c = Customer.create(enterprise: enterprise, email: user2.email.upcase) + expect(c.user).to eq user2 end end end From a1ebd18b7c7e6cbdb438bec6ccf315a7bc7c3344 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Fri, 15 Apr 2016 10:55:31 +0100 Subject: [PATCH 204/215] Updating en-GB file to reflect recent additions --- config/locales/en-GB.yml | 458 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 427 insertions(+), 31 deletions(-) diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 931df49417..06039d9d66 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -21,18 +21,129 @@ en-GB: invalid: | Invalid email or password. Were you a guest last time? Perhaps you need to create an account or reset your password. + enterprise_confirmations: + enterprise: + confirmed: Thankyou, your email address has been confirmed. + not_confirmed: Your email address could not be confirmed. Perhaps you have already completed this step? + confirmation_sent: "Confirmation email sent!" + confirmation_not_sent: "Could not send a confirmation email." home: "OFN" title: Open Food Network welcome_to: 'Welcome to ' + site_meta_description: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can…" search_by_name: Search by name... producers: UK Producers producers_join: UK producers are now welcome to join Open Food Network UK. - charges_sales_tax: Charges sales tax? - print: "Print" + charges_sales_tax: Charges VAT? + print_invoice: "Print Invoice" + send_invoice: "Send Invoice" + resend_confirmation: "Resend Confirmation" + view_order: "View Order" + edit_order: "Edit Order" + ship_order: "Ship Order" + cancel_order: "Cancel Order" + confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" + confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" + invoice: "Invoice" + percentage_of_sales: "%{percentage} of sales" + percentage_of_turnover: "Percentage of turnover" + monthly_cap_excl_tax: "monthly cap (excl. VAT)" + capped_at_cap: "capped at %{cap}" + per_month: "per month" + free: "free" + plus_tax: "plus GST" + total_monthly_bill_incl_tax: "Total Monthly Bill (Incl. Tax)" + say_no: "No" + say_yes: "Yes" - logo: "Logo (640x130)" - logo_mobile: "Mobile logo (75x26)" - logo_mobile_svg: "Mobile logo (SVG)" + sort_order_cycles_on_shopfront_by: "Sort Order Cycles On Shopfront By" + + + admin: + # General form elements + quick_search: Quick Search + clear_all: Clear All + producer: Producer + shop: Shop + product: Product + variant: Variant + + columns: Columns + actions: Actions + viewing: "Viewing: %{current_view_name}" + + whats_this: What's this? + + customers: + index: + add_customer: "Add customer" + customer_placeholder: "customer@example.org" + inventory: + title: Inventory + description: Use this page to manage inventories for your enterprises. Any product details set here will override those set on the 'Products' page + sku: SKU + price: Price + on_hand: On Hand + on_demand: On Demand? + enable_reset: Enable Stock Level Reset? + inherit: Inherit? + add: Add + hide: Hide + select_a_shop: Select A Shop + review_now: Review Now + new_products_alert_message: There are %{new_product_count} new products available to add to your inventory. + currently_empty: Your inventory is currently empty + no_matching_products: No matching products found in your inventory + no_hidden_products: No products have been hidden from this inventory + no_matching_hidden_products: No hidden products match your search criteria + no_new_products: No new products are available to add to this inventory + no_matching_new_products: No new products match your search criteria + inventory_powertip: This is your inventory of products. To add products to your inventory, select 'New Products' from the Viewing dropdown. + hidden_powertip: These products have been hidden from your inventory and will not be available to add to your shop. You can click 'Add' to add a product to you inventory. + new_powertip: These products are available to be added to your inventory. Click 'Add' to add a product to your inventory, or 'Hide' to hide it from view. You can always change your mind later! + + + order_cycle: + choose_products_from: "Choose Products From:" + + enterprise: + select_outgoing_oc_products_from: Select outgoing OC products from + + enterprises: + form: + primary_details: + shopfront_requires_login: "Shopfront requires login?" + shopfront_requires_login_tip: "Choose whether customers must login to view the shopfront." + shopfront_requires_login_false: "Public" + shopfront_requires_login_true: "Require customers to login" + + home: + hubs: + show_closed_shops: "Show closed shops" + hide_closed_shops: "Hide closed shops" + show_on_map: "Show all on the map" + shared: + register_call: + selling_on_ofn: "Interested in getting on the Open Food Network?" + register: "Register here" + shop: + messages: + login: "login" + register: "register" + contact: "contact" + require_customer_login: "This shop is for customers only." + require_login_html: "Please %{login} if you have an account already. Otherwise, %{register} to become a customer." + require_customer_html: "Please %{contact} %{enterprise} to become a customer." + + # Printable Invoice Columns + invoice_column_item: "Item" + invoice_column_qty: "Qty" + invoice_column_tax: "VAT" + invoice_column_price: "Price" + + logo: "Logo (640x130)" #FIXME + logo_mobile: "Mobile logo (75x26)" #FIXME + logo_mobile_svg: "Mobile logo (SVG)" #FIXME home_hero: "Hero image" home_show_stats: "Show statistics" footer_logo: "Logo (220x76)" @@ -46,11 +157,10 @@ en-GB: footer_links_md: "Links" footer_about_url: "About URL" footer_tos_url: "Terms of Service URL" - invoice: "Invoice" name: Name - first_name: First name - last_name: Last name + first_name: First Name + last_name: Last Name email: Email phone: Phone next: Next @@ -90,9 +200,9 @@ en-GB: cart_empty: "Cart empty" cart_edit: "Edit your cart" - card_number: Card number - card_securitycode: "Security code" - card_expiry_date: Expiry date + card_number: Card Number + card_securitycode: "Security Code" + card_expiry_date: Expiry Date ofn_cart_headline: "Current cart for:" ofn_cart_distributor: "Distributor:" @@ -187,7 +297,7 @@ en-GB: checkout_cart_total: Cart total checkout_shipping_price: Shipping checkout_total_price: Total - checkout_back_to_cart: "Back to cart" + checkout_back_to_cart: "Back to Cart" order_paid: PAID order_not_paid: NOT PAID @@ -197,12 +307,32 @@ en-GB: order_delivery_on: Delivery on order_delivery_address: Delivery address order_special_instructions: "Your notes:" - order_pickup_instructions: Collection instructions + order_pickup_time: Ready for collection + order_pickup_instructions: Collection Instructions order_produce: Produce order_total_price: Total order_includes_tax: (includes tax) order_payment_paypal_successful: Your payment via PayPal has been processed successfully. - order_hub_info: Hub info + order_hub_info: Hub Info + + bom_tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." + bom_shared: "Shared Resource?" + bom_page_title: "Bulk Order Management" + bom_no: "Order no." + bom_date: "Order date" + bom_cycle: "Order cycle" + bom_max: "Max" + bom_hub: "Hub" + bom_variant: "Product: Unit" + bom_final_weigth_volume: "Weight/Volume" + bom_quantity: "Quantity" + bom_actions_delete: "Delete Selected" + bom_loading: "Loading orders" + bom_no_results: "No orders found." + bom_order_error: "Some errors must be resolved before you can update orders.\nAny fields with red borders contain errors." + + unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." + unsaved_changes_error: "Fields with red borders contain errors." products: "Products" products_in: "in %{oc}" @@ -307,6 +437,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_cart_empty: "Cart empty" products_edit_cart: "Edit your cart" products_from: from + products_change: "No changes to save." + products_update_error: "Saving failed with the following error(s):" + products_update_error_msg: "Saving failed." + products_update_error_data: "Save failed due to invalid data:" + products_changes_saved: "Changes saved." search_no_results_html: "Sorry, no results found for %{query}. Try another search?" @@ -317,9 +452,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using groups_title: Groups groups_headline: Groups / regions + groups_text: "Every producer is unique. Every business has something different to offer. Our groups are collectives of producers, hubs and distributors who share something in common like location, farmers market or philosophy. This makes your shopping experience easier. So explore our groups and have the curating done for you." groups_search: "Search name or keyword" groups_no_groups: "No groups found" groups_about: "About Us" + groups_producers: "Our producers" groups_hubs: "Our hubs" groups_contact_web: Contact @@ -384,9 +521,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using ocs_close_time: "ORDERS CLOSE" ocs_when_headline: When do you want your order? ocs_when_text: No products are displayed until you select a date. - ocs_when_closing: "Closing on" + ocs_when_closing: "Closing On" ocs_when_choose: "Choose Order Cycle" - ocs_list: "List view" + ocs_list: "List View" producers_about: About us producers_buy: Shop for @@ -407,13 +544,26 @@ See the %{link} to find out more about %{sitename}'s features and to start using producers_signup_cta_headline: Join now! producers_signup_cta_action: Join now producers_signup_detail: Here's the detail. + producer: Producer products_item: Item products_description: Description products_variant: Variant products_quantity: Quantity products_availabel: Available? - products_price: Price + products_producer: "Producer" + products_price: "Price" + products_sku: "SKU" + products_name: "name" + products_unit: "unit" + products_on_hand: "on hand" + products_on_demand: "On demand?" + products_category: "Category" + products_tax_category: "tax category" + products_available_on: "Available On" + products_inherit: "Inherit?" + products_inherits_properties: "Inherits Properties?" + products_stock_level_reset: "Enable Stock Level Reset?" register_title: Register @@ -431,7 +581,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using shops_signup_detail: Here's the detail. orders_fees: Fees... - orders_edit_title: Shopping cart + orders_edit_title: Shopping Cart orders_edit_headline: Your shopping cart orders_edit_time: Order ready for orders_edit_continue: Continue shopping @@ -509,18 +659,17 @@ See the %{link} to find out more about %{sitename}'s features and to start using confirm_password: "Confirm password" action_signup: "Sign up now" welcome_to_ofn: "Welcome to the Open Food Network!" - signup_or_login: "Start By signing up (or logging in)" + signup_or_login: "Start By Signing Up (or logging in)" have_an_account: "Already have an account?" action_login: "Log in now." - forgot_password: "Forgot password?" + forgot_password: "Forgot Password?" password_reset_sent: "An email with instructions on resetting your password has been sent!" reset_password: "Reset password" registration_greeting: "Greetings!" who_is_managing_enterprise: "Who is responsible for managing %{enterprise}?" - enterprise_contact: "Primary contact" + enterprise_contact: "Primary Contact" enterprise_contact_required: "You need to enter a primary contact." - enterprise_email: "Email address" - enterprise_email_required: "You need to enter valid email address." + enterprise_email_address: "Email address" enterprise_phone: "Phone number" back: "Back" continue: "Continue" @@ -528,20 +677,20 @@ See the %{link} to find out more about %{sitename}'s features and to start using limit_reached_message: "You have reached the limit!" limit_reached_text: "You have reached the limit for the number of enterprises you are allowed to own on the" limit_reached_action: "Return to the homepage" - select_promo_image: "Step 3. Select promo image" + select_promo_image: "Step 3. Select Promo Image" promo_image_tip: "Tip: Shown as a banner, preferred size is 1200×260px" promo_image_label: "Choose a promo image" action_or: "OR" promo_image_drag: "Drag and drop your promo here" - review_promo_image: "Step 4. Review your promo banner" + review_promo_image: "Step 4. Review Your Promo Banner" review_promo_image_tip: "Tip: for best results, your promo image should fill the available space" promo_image_placeholder: "Your logo will appear here for review once uploaded" uploading: "Uploading..." - select_logo: "Step 1. Select logo image" + select_logo: "Step 1. Select Logo Image" logo_tip: "Tip: Square images will work best, preferably at least 300×300px" logo_label: "Choose a logo image" logo_drag: "Drag and drop your logo here" - review_logo: "Step 2. Review your logo" + review_logo: "Step 2. Review Your Logo" review_logo_tip: "Tip: for best results, your logo should fill the available space" logo_placeholder: "Your logo will appear here for review once uploaded" enterprise_about_headline: "Nice one!" @@ -598,14 +747,14 @@ Please follow the instructions there to make your enterprise visible on the Open registration_type_error: "Please choose one. Are you are producer?" registration_type_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it." registration_type_no_producer_help: "If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other." - create_profile: "Create profile" + create_profile: "Create Profile" registration_images_headline: "Thanks!" registration_images_description: "Let's upload some pretty pictures so your profile looks great! :)" - registration_detail_headline: "Let's get started" + registration_detail_headline: "Let's Get Started" registration_detail_enterprise: "Woot! First we need to know a little bit about your enterprise:" registration_detail_producer: "Woot! First we need to know a little bit about your farm:" - registration_detail_name_enterprise: "Enterprise name:" - registration_detail_name_producer: "Farm name:" + registration_detail_name_enterprise: "Enterprise Name:" + registration_detail_name_producer: "Farm Name:" registration_detail_name_placeholder: "e.g. Charlie's Awesome Farm" registration_detail_name_error: "Please choose a unique name for your enterprise" registration_detail_address1: "Address line 1:" @@ -641,3 +790,250 @@ Please follow the instructions there to make your enterprise visible on the Open price_graph: "Price graph" included_tax: "Included tax" remove_tax: "Remove tax" + balance: "Balance" + transaction: "Transaction" + transaction_date: "Date" #Transaction is only in key to avoid conflict with :date + payment_state: "Payment status" + shipping_state: "Shipping status" + value: "Value" + balance_due: "Balance due" + credit: "Credit" + Paid: "Paid" + Ready: "Ready" + you_have_no_orders_yet: "You have no orders yet" + running_balance: "Running balance" + outstanding_balance: "Outstanding balance" + admin_entreprise_relationships: "Enterprise Relationships" + admin_entreprise_relationships_everything: "Everything" + admin_entreprise_relationships_permits: "permits" + admin_entreprise_relationships_seach_placeholder: "Search" + admin_entreprise_relationships_button_create: "Create" + admin_entreprise_groups: "Enterprise Groups" + admin_entreprise_groups_name: "Name" + admin_entreprise_groups_owner: "Owner" + admin_entreprise_groups_on_front_page: "On front page ?" + admin_entreprise_groups_entreprise: "Enterprises" + admin_entreprise_groups_primary_details: "Primary Details" + admin_entreprise_groups_data_powertip: "The primary user responsible for this group." + admin_entreprise_groups_data_powertip_logo: "This is the logo for the group" + admin_entreprise_groups_data_powertip_promo_image: "This image is displayed at the top of the Group profile" + admin_entreprise_groups_about: "About" + admin_entreprise_groups_images: "Images" + admin_entreprise_groups_contact: "Contact" + admin_entreprise_groups_contact_phone_placeholder: "eg. 98 7654 3210" + admin_entreprise_groups_contact_address1_placeholder: "eg. 123 High Street" + admin_entreprise_groups_contact_city: "Suburb" + admin_entreprise_groups_contact_city_placeholder: "eg. Northcote" + admin_entreprise_groups_contact_zipcode: "Postcode" + admin_entreprise_groups_contact_zipcode_placeholder: "eg. 3070" + admin_entreprise_groups_contact_state_id: "State" + admin_entreprise_groups_contact_country_id: "Country" + admin_entreprise_groups_web: "Web Resources" + admin_entreprise_groups_web_twitter: "eg. @the_prof" + admin_entreprise_groups_web_website_placeholder: "eg. www.truffles.com" + admin_order_cycles: "Admin Order Cycles" + open: "Open" + close: "Close" + supplier: "Supplier" + coordinator: "Coordinator" + distributor: "Distributor" + product: "Products" + enterprise_fees: "Enterprise Fees" + fee_type: "Fee Type" + tax_category: "Tax Category" + calculator: "Calculator" + calculator_values: "Calculator values" + new_order_cycles: "New Order Cycles" + select_a_coordinator_for_your_order_cycle: "select a coordinator for your order cycle" + edit_order_cycle: "Edit Order Cycle" + roles: "Roles" + update: "Update" + add_producer_property: "Add producer property" + admin_settings: "Settings" + update_invoice: "Update Invoices" + finalise_invoice: "Finalise Invoices" + finalise_user_invoices: "Finalise User Invoices" + finalise_user_invoice_explained: "Use this button to finalize all invoices in the system for the previous calendar month. This task can be set up to run automatically once a month." + manually_run_task: "Manually Run Task " + update_user_invoices: "Update User Invoices" + update_user_invoice_explained: "Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night." + auto_finalise_invoices: "Auto-finalise invoices monthly on the 2nd at 1:30am" + auto_update_invoices: "Auto-update invoices nightly at 1:00am" + in_progress: "In Progress" + started_at: "Started at" + queued: "Queued" + scheduled_for: "Scheduled for" + customers: "Customers" + please_select_hub: "Please select a Hub" + loading_customers: "Loading Customers" + no_customers_found: "No customers found" + go: "Go" + hub: "Hub" + accounts_administration_distributor: "accounts administration distributor" + accounts_and_billing: "Accounts & Billing" + producer: "Producer" + product: "Product" + price: "Price" + on_hand: "On hand" + save_changes: "Save Changes" + spree_admin_overview_enterprises_header: "My Enterprises" + spree_admin_overview_enterprises_footer: "MANAGE MY ENTERPRISES" + spree_admin_enterprises_hubs_name: "Name" + spree_admin_enterprises_create_new: "CREATE NEW" + spree_admin_enterprises_shipping_methods: "Shipping Methods" + spree_admin_enterprises_fees: "Enterprise Fees" + spree_admin_enterprises_none_create_a_new_enterprise: "CREATE A NEW ENTERPRISE" + spree_admin_enterprises_none_text: "You don't have any enterprises yet" + spree_admin_enterprises_producers_name: "Name" + spree_admin_enterprises_producers_total_products: "Total Products" + spree_admin_enterprises_producers_active_products: "Active Products" + spree_admin_enterprises_producers_order_cycles: "Products in OCs" + spree_admin_enterprises_producers_order_cycles_title: "" + spree_admin_enterprises_tabs_hubs: "HUBS" + spree_admin_enterprises_tabs_producers: "PRODUCERS" + spree_admin_enterprises_producers_manage_order_cycles: "MANAGE ORDER CYCLES" + spree_admin_enterprises_producers_manage_products: "MANAGE PRODUCTS" + spree_admin_enterprises_producers_orders_cycle_text: "You don't have any active order cycles." + spree_admin_enterprises_any_active_products_text: "You don't have any active products." + spree_admin_enterprises_create_new_product: "CREATE A NEW PRODUCT" + spree_admin_order_cycles: "Order Cycles" + spree_admin_order_cycles_tip: "Order cycles determine when and where your products are available to customers." + dashbord: "Dashboard" + spree_admin_single_enterprise_alert_mail_confirmation: "Please confirm the email address for" + spree_admin_single_enterprise_alert_mail_sent: "We've sent an email to" + spree_admin_overview_action_required: "Action Required" + spree_admin_overview_check_your_inbox: "Please check you inbox for furher instructions. Thanks!" + change_package: "Change Package" + spree_admin_single_enterprise_hint: "Hint: To allow people to find you, turn on your visibility under" + your_profil_live: "Your profile live" + on_ofn_map: "on the Open Food Network map" + see: "See" + live: "live" + manage: "Manage" + resend: "Resend" + add_and_manage_products: "Add & manage products" + add_and_manage_order_cycles: "Add & manage order cycles" + manage_order_cycles: "Manage order cycles" + manage_products: "Manage products" + edit_profile_details: "Edit profile details" + edit_profile_details_etc: "Change your profile description, images, etc." + start_date: "Start Date" + end_date: "End Date" + order_cycle: "Order Cycle" + group_buy_unit_size: "Group Buy Unit Size" + total_qtt_ordered: "Total Quantity Ordered" + max_qtt_ordered: "Max Quantity Ordered" + current_fulfilled_units: "Current Fulfilled Units" + max_fulfilled_units: "Max Fulfilled Units" + bulk_management_warning: "WARNING: Some variants do not have a unit value" + ask: "Ask?" + no_orders_found: "No orders found." + order_no: "Order No." + weight_volume: "Weight/Volume" + remove_tax: "Remove tax" + tax_settings: "Tax Settings" + products_require_tax_category: "products require tax category" + admin_shared_address_1: "Address" + admin_shared_address_2: "Address (cont.)" + admin_share_city: "City" + admin_share_zipcode: "Postcode" + admin_share_country: "Country" + admin_share_state: "State" + hub_sidebar_hubs: "Hubs" + hub_sidebar_none_available: "None Available" + hub_sidebar_manage: "Manage" + hub_sidebar_at_least: "At least one hub must be selected" + hub_sidebar_blue: "blue" + hub_sidebar_red: "red" + shop_trial_in_progress: "Your shopfront trial expires in %{days}." + shop_trial_expired: "Good news! We have decided to extend shopfront trials until further notice (probably around March 2015)." #FIXME + report_customers_distributor: "Distributor" + report_customers_supplier: "Supplier" + report_customers_cycle: "Order Cycle" + report_customers_type: "Report Type" + report_customers_csv: "Download as csv" + report_producers: "Producers: " + report_type: "Report Type: " + report_hubs: "Hubs: " + report_payment: "Payment Methods: " + report_distributor: "Distributor: " + report_payment_by: 'Payments By Type' + report_itemised_payment: 'Itemised Payment Totals' + report_payment_totals: 'Payment Totals' + report_all: 'all' + report_order_cycle: "Order Cycle: " + report_entreprises: "Enterprises: " + report_users: "Users: " + initial_invoice_number: "Initial invoice number:" + invoice_date: "Invoice date:" + due_date: "Due date:" + account_code: "Account code:" + equals: "Equals" + contains: "contains" + discount: "Discount" + filter_products: "Filter Products" + delete_product_variant: "The last variant cannot be deleted!" + progress: "progress" + saving: "Saving.." + success: "success" + failure: "failure" + unsaved_changes_confirmation: "Unsaved changes will be lost. Continue anyway?" + one_product_unsaved: "Changes to one product remain unsaved." + products_unsaved: "Changes to %{n} products remain unsaved." + add_manager: "Add a manager" + is_already_manager: "is already a manager!" + no_change_to_save: " No change to save" + add_manager: "Add a manager" + users: "Users" + about: "About" + images: "Images" + contact: "Contact" + web: "Web" + primary_details: "Primary Details" + adrdress: "Address" + contact: "Contact" + social: "Social" + business_details: "Business Details" + properties: "Properties" + shipping_methods: "Shipping Methods" + payment_methods: "Payment Methods" + enterprise_fees: "Enterprise Fees" + inventory_settings: "Inventory Settings" + tag_rules: "Tag Rules" + shop_preferences: "Shop Preferences" + validation_msg_relationship_already_established: "^That relationship is already established." + validation_msg_at_least_one_hub: "^At least one hub must be selected" + validation_msg_product_category_cant_be_blank: "^Product Category cant be blank" + validation_msg_tax_category_cant_be_blank: "^Tax Category can't be blank" + validation_msg_is_associated_with_an_exising_customer: "is associated with an existing customer" + + spree: + shipment_states: + backorder: backorder + partial: partial + pending: pending + ready: ready + shipped: shipped + payment_states: + balance_due: balance due + completed: completed + checkout: checkout + credit_owed: credit owed + failed: failed + paid: paid + pending: pending + processing: processing + void: void + order_state: + address: address + adjustments: adjustments + awaiting_return: awaiting return + canceled: canceled + cart: cart + complete: complete + confirm: confirm + delivery: delivery + payment: payment + resumed: resumed + returned: returned + skrill: skrill From abfb8149d93f9964262820f94fcdec621269f0ce Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Wed, 16 Mar 2016 11:35:31 +0000 Subject: [PATCH 205/215] Adding configurable Shop Trial Length in business model --- .../admin/business_model_configuration_controller.rb | 3 ++- app/helpers/enterprises_helper.rb | 6 +++--- app/models/enterprise.rb | 3 +-- app/models/spree/app_configuration_decorator.rb | 2 ++ .../admin/business_model_configuration/edit.html.haml | 6 ++++++ .../business_model_configuration_validator.rb | 3 ++- .../business_model_configuration_controller_spec.rb | 9 +++++++-- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/app/controllers/admin/business_model_configuration_controller.rb b/app/controllers/admin/business_model_configuration_controller.rb index 312a2e3208..38506e8368 100644 --- a/app/controllers/admin/business_model_configuration_controller.rb +++ b/app/controllers/admin/business_model_configuration_controller.rb @@ -18,8 +18,9 @@ class Admin::BusinessModelConfigurationController < Spree::Admin::BaseController def load_settings @settings = OpenFoodNetwork::BusinessModelConfigurationValidator.new(params[:settings] || { + shop_trial_length_days: Spree::Config[:shop_trial_length_days], account_invoices_monthly_fixed: Spree::Config[:account_invoices_monthly_fixed], - account_invoices_monthly_rate: Spree::Config[:account_invoices_monthly_rate], + account_invoiceaccount_invoices_monthly_rates_monthly_rate: Spree::Config[:account_invoices_monthly_rate], account_invoices_monthly_cap: Spree::Config[:account_invoices_monthly_cap], account_invoices_tax_rate: Spree::Config[:account_invoices_tax_rate] }) diff --git a/app/helpers/enterprises_helper.rb b/app/helpers/enterprises_helper.rb index 5aa36624cd..93b7c96f95 100644 --- a/app/helpers/enterprises_helper.rb +++ b/app/helpers/enterprises_helper.rb @@ -52,17 +52,17 @@ module EnterprisesHelper def shop_trial_in_progress?(enterprise) !!enterprise.shop_trial_start_date && - (enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days > Time.zone.now) && + (enterprise.shop_trial_start_date + Spree::Config[:shop_trial_length_days].days > Time.zone.now) && %w(own any).include?(enterprise.sells) end def shop_trial_expired?(enterprise) !!enterprise.shop_trial_start_date && - (enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days <= Time.zone.now) && + (enterprise.shop_trial_start_date + Spree::Config[:shop_trial_length_days].days <= Time.zone.now) && %w(own any).include?(enterprise.sells) end def remaining_trial_days(enterprise) - distance_of_time_in_words(Time.zone.now, enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days) + distance_of_time_in_words(Time.zone.now, enterprise.shop_trial_start_date + Spree::Config[:shop_trial_length_days].days) end end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index dfc5050a38..9f839f9d08 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -1,6 +1,5 @@ class Enterprise < ActiveRecord::Base SELLS = %w(unspecified none own any) - SHOP_TRIAL_LENGTH = 30 ENTERPRISE_SEARCH_RADIUS = 100 preference :shopfront_message, :text, default: "" @@ -338,7 +337,7 @@ class Enterprise < ActiveRecord::Base end def shop_trial_expiry - shop_trial_start_date.andand + Enterprise::SHOP_TRIAL_LENGTH.days + shop_trial_start_date.andand + Spree::Config[:shop_trial_length_days].days end def can_invoice? diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index 6ef1e7b848..80988ce7c1 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -20,7 +20,9 @@ Spree::AppConfiguration.class_eval do preference :account_invoices_monthly_rate, :decimal, default: 0 preference :account_invoices_monthly_cap, :decimal, default: 0 preference :account_invoices_tax_rate, :decimal, default: 0 + preference :shop_trial_length_days, :integer, default: 30 # Monitoring preference :last_job_queue_heartbeat_at, :string, default: nil + end diff --git a/app/views/admin/business_model_configuration/edit.html.haml b/app/views/admin/business_model_configuration/edit.html.haml index 89345178f9..91c562c81b 100644 --- a/app/views/admin/business_model_configuration/edit.html.haml +++ b/app/views/admin/business_model_configuration/edit.html.haml @@ -17,6 +17,12 @@ Adjust the amount that enterprises will be billed each month for use of the OFN. %br = form_for @settings, as: :settings, url: main_app.admin_business_model_configuration_path, :method => :put do |f| + .row + .three.columns.alpha + = f.label :shop_trial_length_days, t(:shop_trial_length) + %span.icon-question-sign{'ofn-with-tip' => "The length of time (in days) that enterprises who are set up as shops can run as a trial period."} + .two.columns.omega + = f.number_field :shop_trial_length_days, min: 0.0, step: 1.0, class: "fullwidth", 'watch-value-as' => 'fixed' .row .three.columns.alpha = f.label :account_invoices_monthly_fixed, t(:fixed_monthly_charge) diff --git a/lib/open_food_network/business_model_configuration_validator.rb b/lib/open_food_network/business_model_configuration_validator.rb index d83d94ffc1..0d6b4d9f83 100644 --- a/lib/open_food_network/business_model_configuration_validator.rb +++ b/lib/open_food_network/business_model_configuration_validator.rb @@ -5,8 +5,9 @@ module OpenFoodNetwork class BusinessModelConfigurationValidator include ActiveModel::Validations - attr_accessor :account_invoices_monthly_fixed, :account_invoices_monthly_rate, :account_invoices_monthly_cap, :account_invoices_tax_rate + attr_accessor :shop_trial_length_days, :account_invoices_monthly_fixed, :account_invoices_monthly_rate, :account_invoices_monthly_cap, :account_invoices_tax_rate + validates :shop_trial_length_days, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :account_invoices_monthly_fixed, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :account_invoices_monthly_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 } validates :account_invoices_monthly_cap, presence: true, numericality: { greater_than_or_equal_to: 0 } diff --git a/spec/controllers/admin/business_model_configuration_controller_spec.rb b/spec/controllers/admin/business_model_configuration_controller_spec.rb index b0ae86d4ac..9c970c33f9 100644 --- a/spec/controllers/admin/business_model_configuration_controller_spec.rb +++ b/spec/controllers/admin/business_model_configuration_controller_spec.rb @@ -9,7 +9,8 @@ describe Admin::BusinessModelConfigurationController, type: :controller do account_invoices_monthly_fixed: 5, account_invoices_monthly_rate: 0.02, account_invoices_monthly_cap: 50, - account_invoices_tax_rate: 0.1 + account_invoices_tax_rate: 0.1, + shop_trial_length_days: 30 }) end @@ -53,16 +54,18 @@ describe Admin::BusinessModelConfigurationController, type: :controller do params[:settings][:account_invoices_monthly_rate] = '2' params[:settings][:account_invoices_monthly_cap] = '-1' params[:settings][:account_invoices_tax_rate] = '4' + params[:settings][:shop_trial_length_days] = '-30' spree_get :update, params end it "does not allow them to be set" do expect(response).to render_template :edit - expect(assigns(:settings).errors.count).to be 5 + expect(assigns(:settings).errors.count).to be 6 expect(Spree::Config.account_invoices_monthly_fixed).to eq 5 expect(Spree::Config.account_invoices_monthly_rate).to eq 0.02 expect(Spree::Config.account_invoices_monthly_cap).to eq 50 expect(Spree::Config.account_invoices_tax_rate).to eq 0.1 + expect(Spree::Config.shop_trial_length_days).to eq 30 end end @@ -72,6 +75,7 @@ describe Admin::BusinessModelConfigurationController, type: :controller do params[:settings][:account_invoices_monthly_rate] = '0.05' params[:settings][:account_invoices_monthly_cap] = '30' params[:settings][:account_invoices_tax_rate] = '0.15' + params[:settings][:shop_trial_length_days] = '20' end it "sets global config to the specified values" do @@ -81,6 +85,7 @@ describe Admin::BusinessModelConfigurationController, type: :controller do expect(Spree::Config.account_invoices_monthly_rate).to eq 0.05 expect(Spree::Config.account_invoices_monthly_cap).to eq 30 expect(Spree::Config.account_invoices_tax_rate).to eq 0.15 + expect(Spree::Config.shop_trial_length_days).to eq 20 end end end From 4fc33c7da2e0d6dae06ea33e982a20ecd50957bd Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Wed, 16 Mar 2016 11:38:00 +0000 Subject: [PATCH 206/215] Fixing wild typo --- .../admin/business_model_configuration_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/business_model_configuration_controller.rb b/app/controllers/admin/business_model_configuration_controller.rb index 38506e8368..967238b347 100644 --- a/app/controllers/admin/business_model_configuration_controller.rb +++ b/app/controllers/admin/business_model_configuration_controller.rb @@ -20,7 +20,7 @@ class Admin::BusinessModelConfigurationController < Spree::Admin::BaseController @settings = OpenFoodNetwork::BusinessModelConfigurationValidator.new(params[:settings] || { shop_trial_length_days: Spree::Config[:shop_trial_length_days], account_invoices_monthly_fixed: Spree::Config[:account_invoices_monthly_fixed], - account_invoiceaccount_invoices_monthly_rates_monthly_rate: Spree::Config[:account_invoices_monthly_rate], + account_invoices_monthly_rate: Spree::Config[:account_invoices_monthly_rate], account_invoices_monthly_cap: Spree::Config[:account_invoices_monthly_cap], account_invoices_tax_rate: Spree::Config[:account_invoices_tax_rate] }) From 51629cd0c04b24b8987adc5cd82053430b352f2b Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Wed, 16 Mar 2016 12:35:45 +0000 Subject: [PATCH 207/215] Adding translations for shop front trials --- app/views/spree/admin/shared/_trial_progress_bar.html.haml | 4 ++-- config/locales/en-GB.yml | 4 ++++ config/locales/en.yml | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/views/spree/admin/shared/_trial_progress_bar.html.haml b/app/views/spree/admin/shared/_trial_progress_bar.html.haml index 3deef097fe..4c76e40705 100644 --- a/app/views/spree/admin/shared/_trial_progress_bar.html.haml +++ b/app/views/spree/admin/shared/_trial_progress_bar.html.haml @@ -1,7 +1,7 @@ - if enterprise -if shop_trial_in_progress?(enterprise) #trial_progress_bar - = t(:shop_trial_in_progress, days: remaining_trial_days(enterprise)) + = "#{t(:shop_trial_expires_in)} #{remaining_trial_days(enterprise)}." -elsif shop_trial_expired?(enterprise) #trial_progress_bar - = t(:shop_trial_expired) + = t(:shop_trial_expired_notice) diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 06039d9d66..58441e9934 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -1037,3 +1037,7 @@ Please follow the instructions there to make your enterprise visible on the Open resumed: resumed returned: returned skrill: skrill + shop_trial_length: "Shop Trial Length (Days)" + shop_trial_length: "Shop Trial Length (Days)" + shop_trial_expires_in: "Your shopfront trial expires in" + shop_trial_expired_notice: "Open Food Network UK is currently free while we prepare for our soft launch in April, 2016." diff --git a/config/locales/en.yml b/config/locales/en.yml index 7b5f9b168f..34c4f4d4e9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1041,3 +1041,6 @@ Please follow the instructions there to make your enterprise visible on the Open resumed: resumed returned: returned skrill: skrill + shop_trial_length: "Shop Trial Length (Days)" + shop_trial_expires_in: "Your shopfront trial expires in" + shop_trial_expired_notice: "Good news! We have decided to extend shopfront trials until further notice (probably around March 2015)." From 4033a7888777a4a06fe59134d1ba66fbf603c969 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Wed, 16 Mar 2016 14:17:43 +0000 Subject: [PATCH 208/215] Removing incorrect angular watch --- app/views/admin/business_model_configuration/edit.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/business_model_configuration/edit.html.haml b/app/views/admin/business_model_configuration/edit.html.haml index 91c562c81b..4794c536b4 100644 --- a/app/views/admin/business_model_configuration/edit.html.haml +++ b/app/views/admin/business_model_configuration/edit.html.haml @@ -22,7 +22,7 @@ = f.label :shop_trial_length_days, t(:shop_trial_length) %span.icon-question-sign{'ofn-with-tip' => "The length of time (in days) that enterprises who are set up as shops can run as a trial period."} .two.columns.omega - = f.number_field :shop_trial_length_days, min: 0.0, step: 1.0, class: "fullwidth", 'watch-value-as' => 'fixed' + = f.number_field :shop_trial_length_days, min: 0.0, step: 1.0, class: "fullwidth" .row .three.columns.alpha = f.label :account_invoices_monthly_fixed, t(:fixed_monthly_charge) From 44ac44e1ddccd86fe7a3661dc774370c447a6c76 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Thu, 17 Mar 2016 11:15:06 +0000 Subject: [PATCH 209/215] Updating Bill Calculator to include a minimum billable turnover. Shopfronts are not charged if their tunrover is under the minimum billable. --- lib/open_food_network/bill_calculator.rb | 4 +- spec/models/billable_period_spec.rb | 395 +++++++++++++++++++---- 2 files changed, 339 insertions(+), 60 deletions(-) diff --git a/lib/open_food_network/bill_calculator.rb b/lib/open_food_network/bill_calculator.rb index 84457d6530..d5302c0996 100644 --- a/lib/open_food_network/bill_calculator.rb +++ b/lib/open_food_network/bill_calculator.rb @@ -1,6 +1,6 @@ module OpenFoodNetwork class BillCalculator - attr_accessor :turnover, :fixed, :rate, :cap, :tax_rate + attr_accessor :turnover, :fixed, :rate, :cap, :tax_rate, :min_bill_to def initialize(opts={}) @turnover = opts[:turnover] || 0 @@ -8,11 +8,13 @@ module OpenFoodNetwork @rate = opts[:rate] || Spree::Config[:account_invoices_monthly_rate] @cap = opts[:cap] || Spree::Config[:account_invoices_monthly_cap] @tax_rate = opts[:tax_rate] || Spree::Config[:account_invoices_tax_rate] + @min_bill_to = opts[:min_bill_to] || Spree::Config[:minimum_billable_turnover] end def bill bill = fixed + (turnover * rate) bill = cap > 0 ? [bill, cap].min : bill + bill = turnover > min_bill_to ? bill : 0 bill * (1 + tax_rate) end end diff --git a/spec/models/billable_period_spec.rb b/spec/models/billable_period_spec.rb index 3037ebc6bc..b2a1b4470e 100644 --- a/spec/models/billable_period_spec.rb +++ b/spec/models/billable_period_spec.rb @@ -34,94 +34,371 @@ describe BillablePeriod, type: :model do context "when no tax is charged" do before { Spree::Config.set(:account_invoices_tax_rate, 0) } - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + context "when no minimum billable turnover" do + before { Spree::Config.set(:minimum_billable_turnover, 0) } - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - context "when the bill is capped" do - context "at a level higher than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 65) } - it { expect(subject.bill).to eq 60 } + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 65) } + it { expect(subject.bill).to eq 60 } + end + + context "at a level lower than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 55) } + it { expect(subject.bill).to eq 55 } + end end - context "at a level lower than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 55) } - it { expect(subject.bill).to eq 55 } + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 60 } end end - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 60 } + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 15) } + it { expect(subject.bill).to eq 10 } + end + + context "at a level lower than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 5) } + it { expect(subject.bill).to eq 5 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 10 } + end end end - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + context "when a fixed cost is not included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - context "when the bill is capped" do - context "at a level higher than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 15) } - it { expect(subject.bill).to eq 10 } + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 55) } + it { expect(subject.bill).to eq 50 } + end + + context "at a level lower than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 45) } + it { expect(subject.bill).to eq 45 } + end end - context "at a level lower than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 5) } - it { expect(subject.bill).to eq 5 } + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 50 } end end - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 10 } + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(subject.bill).to eq 0 } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end end end end - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + context "when turnover is above minimum billable turnover" do + before { Spree::Config.set(:minimum_billable_turnover, 99) } - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - context "when the bill is capped" do - context "at a level higher than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 55) } - it { expect(subject.bill).to eq 50 } + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 65) } + it { expect(subject.bill).to eq 60 } + end + + context "at a level lower than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 55) } + it { expect(subject.bill).to eq 55 } + end end - context "at a level lower than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 45) } - it { expect(subject.bill).to eq 45 } + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 60 } end end - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 50 } + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 15) } + it { expect(subject.bill).to eq 10 } + end + + context "at a level lower than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 5) } + it { expect(subject.bill).to eq 5 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 10 } + end end end - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + context "when a fixed cost is not included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(subject.bill).to eq 0 } + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 55) } + it { expect(subject.bill).to eq 50 } + end + + context "at a level lower than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 45) } + it { expect(subject.bill).to eq 45 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 50 } + end end - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(subject.bill).to eq 0 } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end + end + end + end + + context "when turnover is below minimum billable turnover" do + before { Spree::Config.set(:minimum_billable_turnover, 101) } + + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 65) } + it { expect(subject.bill).to eq 0 } + end + + context "at a level lower than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 55) } + it { expect(subject.bill).to eq 0 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 15) } + it { expect(subject.bill).to eq 0 } + end + + context "at a level lower than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 5) } + it { expect(subject.bill).to eq 0 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end + end + end + + context "when a fixed cost is not included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 55) } + it { expect(subject.bill).to eq 0 } + end + + context "at a level lower than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 45) } + it { expect(subject.bill).to eq 0 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(subject.bill).to eq 0 } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end + end + end + end + + context "when tax is charged" do + before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } + + context "when turnover is above minimum billable turnover" do + before { Spree::Config.set(:minimum_billable_turnover, 99) } + + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 61) } + it { expect(subject.bill).to eq 66 } + end + + context "at a level lower than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 59) } + it { + expect(subject.bill.to_f).to eq 64.9 + } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 66 } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 11) } + it { expect(subject.bill).to eq 11 } + end + + context "at a level lower than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 9) } + it { expect(subject.bill.to_f).to eq 9.9 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 11 } + end + end + end + + context "when a fixed cost is not included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 51) } + it { expect(subject.bill).to eq 55 } + end + + context "at a level lower than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 49) } + it { expect(subject.bill.to_f).to eq 53.9 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 55 } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(subject.bill).to eq 0 } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end + end end end end end - context "when tax is charged" do - before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } + context "when turnover is below minimum billable turnover" do + before { Spree::Config.set(:minimum_billable_turnover, 101) } context "when a fixed cost is included" do before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } @@ -132,20 +409,20 @@ describe BillablePeriod, type: :model do context "when the bill is capped" do context "at a level higher than the fixed charge plus the product of the rate and turnover" do before { Spree::Config.set(:account_invoices_monthly_cap, 61) } - it { expect(subject.bill).to eq 66 } + it { expect(subject.bill).to eq 0 } end context "at a level lower than the fixed charge plus the product of the rate and turnover" do before { Spree::Config.set(:account_invoices_monthly_cap, 59) } it { - expect(subject.bill.to_f).to eq 64.9 + expect(subject.bill.to_f).to eq 0 } end end context "when the bill is not capped" do before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 66 } + it { expect(subject.bill).to eq 0 } end end @@ -155,18 +432,18 @@ describe BillablePeriod, type: :model do context "when the bill is capped" do context "at a level higher than the fixed charge" do before { Spree::Config.set(:account_invoices_monthly_cap, 11) } - it { expect(subject.bill).to eq 11 } + it { expect(subject.bill).to eq 0 } end context "at a level lower than the fixed charge" do before { Spree::Config.set(:account_invoices_monthly_cap, 9) } - it { expect(subject.bill.to_f).to eq 9.9 } + it { expect(subject.bill.to_f).to eq 0 } end end context "when the bill is not capped" do before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 11 } + it { expect(subject.bill).to eq 0 } end end end @@ -180,18 +457,18 @@ describe BillablePeriod, type: :model do context "when the bill is capped" do context "at a level higher than the product of the rate and turnover" do before { Spree::Config.set(:account_invoices_monthly_cap, 51) } - it { expect(subject.bill).to eq 55 } + it { expect(subject.bill).to eq 0 } end context "at a level lower than the product of the rate and turnover" do before { Spree::Config.set(:account_invoices_monthly_cap, 49) } - it { expect(subject.bill.to_f).to eq 53.9 } + it { expect(subject.bill.to_f).to eq 0 } end end context "when the bill is not capped" do before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 55 } + it { expect(subject.bill).to eq 0 } end end From af4c8bee94e7735baf555bb42abc6be2b878a3e1 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Thu, 17 Mar 2016 12:22:27 +0000 Subject: [PATCH 210/215] Adding preference def for minimum_billable_turnover --- app/models/spree/app_configuration_decorator.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index 80988ce7c1..840ce4f6a4 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -21,6 +21,7 @@ Spree::AppConfiguration.class_eval do preference :account_invoices_monthly_cap, :decimal, default: 0 preference :account_invoices_tax_rate, :decimal, default: 0 preference :shop_trial_length_days, :integer, default: 30 + preference :minimum_billable_turnover, :integer, default: -1 # Monitoring preference :last_job_queue_heartbeat_at, :string, default: nil From ae88a9c2e1dc78016ed6fd4bb3575737e1865472 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Thu, 17 Mar 2016 12:24:34 +0000 Subject: [PATCH 211/215] Updating spec with minimum_billable_turnover preference --- spec/jobs/update_account_invoices_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/jobs/update_account_invoices_spec.rb b/spec/jobs/update_account_invoices_spec.rb index 87d601b60a..60b26248cc 100644 --- a/spec/jobs/update_account_invoices_spec.rb +++ b/spec/jobs/update_account_invoices_spec.rb @@ -12,6 +12,7 @@ describe UpdateAccountInvoices do Spree::Config.set(:account_invoices_monthly_fixed, 5) Spree::Config.set(:account_invoices_monthly_rate, 0.02) Spree::Config.set(:account_invoices_monthly_cap, 50) + Spree::Config.set(:minimum_billable_turnover, -1) end describe "units specs" do From 240be2be0f1ed6457f93395a1df283b7d04205ee Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Thu, 17 Mar 2016 12:36:38 +0000 Subject: [PATCH 212/215] Updating specs to explore the cases around zero turnover with fixed rate and minimum billable turnover --- spec/models/billable_period_spec.rb | 69 ++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/spec/models/billable_period_spec.rb b/spec/models/billable_period_spec.rb index b2a1b4470e..e79c0a2cd2 100644 --- a/spec/models/billable_period_spec.rb +++ b/spec/models/billable_period_spec.rb @@ -27,6 +27,73 @@ describe BillablePeriod, type: :model do end end + describe "calculating monthly bills for enterprises with no turnover" do + let!(:subject) { create(:billable_period, turnover: 0) } + + context "when no tax is charged" do + before { Spree::Config.set(:account_invoices_tax_rate, 0) } + + context "when no minimum billable turnover" do + before { Spree::Config.set(:minimum_billable_turnover, -1) } + + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + it { expect(subject.bill).to eq 10 } + end + + context "when no fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + it { expect(subject.bill).to eq 0 } + end + end + + context "when minimum billable turnover exists" do + before { Spree::Config.set(:minimum_billable_turnover, 0) } + + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + it { expect(subject.bill).to eq 0 } + end + + context "when no fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + it { expect(subject.bill).to eq 0 } + end + end + end + + context "when tax is charged" do + before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } + + context "when no minimum billable turnover" do + before { Spree::Config.set(:minimum_billable_turnover, -1) } + + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + it { expect(subject.bill).to eq 11 } + end + + context "when no fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + it { expect(subject.bill).to eq 0 } + end + end + + context "when minimum billable turnover exists" do + before { Spree::Config.set(:minimum_billable_turnover, 0) } + + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + it { expect(subject.bill).to eq 0 } + end + + context "when no fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + it { expect(subject.bill).to eq 0 } + end + end + end + end describe "calculating monthly bills for enterprises" do let!(:subject) { create(:billable_period, turnover: 100) } @@ -35,7 +102,7 @@ describe BillablePeriod, type: :model do before { Spree::Config.set(:account_invoices_tax_rate, 0) } context "when no minimum billable turnover" do - before { Spree::Config.set(:minimum_billable_turnover, 0) } + before { Spree::Config.set(:minimum_billable_turnover, -1) } context "when a fixed cost is included" do before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } From 30ed6df38d68931ea8b52fc72afb84cdc4fc551b Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Thu, 17 Mar 2016 12:54:03 +0000 Subject: [PATCH 213/215] Adding super admin configuration for the minimum billable turnover functionality --- .../business_model_configuration_controller.js.coffee | 6 +++++- .../admin/business_model_configuration_controller.rb | 4 +++- .../admin/business_model_configuration/edit.html.haml | 10 +++++++++- .../business_model_configuration_validator.rb | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee b/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee index ca757c673d..8229def620 100644 --- a/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee +++ b/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee @@ -9,6 +9,10 @@ angular.module("admin.businessModelConfiguration").controller "BusinessModelConf return $scope.bill() if !$scope.cap? || Number($scope.cap) == 0 Math.min($scope.bill(), Number($scope.cap)) + $scope.finalBill = -> + return 0 if Number($scope.turnover) <= Number($scope.min_bill_to) + $scope.cappedBill() + $scope.capReached = -> return "No" if !$scope.cap? || Number($scope.cap) == 0 if $scope.bill() >= Number($scope.cap) then "Yes" else "No" @@ -18,4 +22,4 @@ angular.module("admin.businessModelConfiguration").controller "BusinessModelConf ($scope.cappedBill() * Number($scope.taxRate)) $scope.total = -> - $scope.cappedBill() + $scope.includedTax() + $scope.finalBill() + $scope.includedTax() diff --git a/app/controllers/admin/business_model_configuration_controller.rb b/app/controllers/admin/business_model_configuration_controller.rb index 967238b347..b3c3c67ab8 100644 --- a/app/controllers/admin/business_model_configuration_controller.rb +++ b/app/controllers/admin/business_model_configuration_controller.rb @@ -22,7 +22,9 @@ class Admin::BusinessModelConfigurationController < Spree::Admin::BaseController account_invoices_monthly_fixed: Spree::Config[:account_invoices_monthly_fixed], account_invoices_monthly_rate: Spree::Config[:account_invoices_monthly_rate], account_invoices_monthly_cap: Spree::Config[:account_invoices_monthly_cap], - account_invoices_tax_rate: Spree::Config[:account_invoices_tax_rate] + account_invoices_tax_rate: Spree::Config[:account_invoices_tax_rate], + minimum_billable_turnover: Spree::Config[:minimum_billable_turnover] + }) end diff --git a/app/views/admin/business_model_configuration/edit.html.haml b/app/views/admin/business_model_configuration/edit.html.haml index 4794c536b4..8a5c368444 100644 --- a/app/views/admin/business_model_configuration/edit.html.haml +++ b/app/views/admin/business_model_configuration/edit.html.haml @@ -26,7 +26,7 @@ .row .three.columns.alpha = f.label :account_invoices_monthly_fixed, t(:fixed_monthly_charge) - %span.icon-question-sign{'ofn-with-tip' => "A fixed monthly charge for ALL enterprises who are set up as a shop, regardless of how much produce they sell."} + %span.icon-question-sign{'ofn-with-tip' => "A fixed monthly charge for all enterprises who are set up as a shop and have exceeded the minimum billable turnover (if set)."} .two.columns.omega .input-symbol.before %span= Spree::Money.currency_symbol @@ -51,6 +51,14 @@ %span.icon-question-sign{'ofn-with-tip' => "Tax rate that applies to the the monthly bill that enterprises are charged for using the system."} .two.columns.omega = f.number_field :account_invoices_tax_rate, min: 0.0, max: 1.0, step: 0.01, class: "fullwidth", 'watch-value-as' => 'taxRate' + .row + .three.columns.alpha + = f.label :minimum_billable_turnover, t(:minimum_monthly_billable_turnover) + %span.icon-question-sign{'ofn-with-tip' => "Minimum monthly turnover before a shopfront will be charged for using OFN. Enterprises turning over less than this amount in a month will not be charged, either as a percentage or fixed rate. When set to -1 enterprises with no turnover will be charge the fixed rate as specified."} + .two.columns.omega + .input-symbol.before + %span= Spree::Money.currency_symbol + = f.number_field :minimum_billable_turnover, min: -1.0, class: "fullwidth", 'watch-value-as' => 'min_bill_to' .row .five.columns.alpha.omega.form-buttons{"data-hook" => "buttons"} diff --git a/lib/open_food_network/business_model_configuration_validator.rb b/lib/open_food_network/business_model_configuration_validator.rb index 0d6b4d9f83..1e1c077808 100644 --- a/lib/open_food_network/business_model_configuration_validator.rb +++ b/lib/open_food_network/business_model_configuration_validator.rb @@ -5,13 +5,14 @@ module OpenFoodNetwork class BusinessModelConfigurationValidator include ActiveModel::Validations - attr_accessor :shop_trial_length_days, :account_invoices_monthly_fixed, :account_invoices_monthly_rate, :account_invoices_monthly_cap, :account_invoices_tax_rate + attr_accessor :shop_trial_length_days, :account_invoices_monthly_fixed, :account_invoices_monthly_rate, :account_invoices_monthly_cap, :account_invoices_tax_rate, :minimum_billable_turnover validates :shop_trial_length_days, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :account_invoices_monthly_fixed, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :account_invoices_monthly_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 } validates :account_invoices_monthly_cap, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :account_invoices_tax_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 } + validates :minimum_billable_turnover, presence: true, numericality: { greater_than_or_equal_to: -1 } def initialize(attr, button=nil) attr.each { |k,v| instance_variable_set("@#{k}", v) } From 6884f5533eceb46a7cab40d5630c72bbf4635f40 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Thu, 17 Mar 2016 13:40:25 +0000 Subject: [PATCH 214/215] Missing Specs --- .../business_model_configuration_controller_spec.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/controllers/admin/business_model_configuration_controller_spec.rb b/spec/controllers/admin/business_model_configuration_controller_spec.rb index 9c970c33f9..4bbf831bb6 100644 --- a/spec/controllers/admin/business_model_configuration_controller_spec.rb +++ b/spec/controllers/admin/business_model_configuration_controller_spec.rb @@ -10,7 +10,8 @@ describe Admin::BusinessModelConfigurationController, type: :controller do account_invoices_monthly_rate: 0.02, account_invoices_monthly_cap: 50, account_invoices_tax_rate: 0.1, - shop_trial_length_days: 30 + shop_trial_length_days: 30, + minimum_billable_turnover: -1 }) end @@ -55,17 +56,19 @@ describe Admin::BusinessModelConfigurationController, type: :controller do params[:settings][:account_invoices_monthly_cap] = '-1' params[:settings][:account_invoices_tax_rate] = '4' params[:settings][:shop_trial_length_days] = '-30' + params[:settings][:minimum_billable_turnover] = '-2' spree_get :update, params end it "does not allow them to be set" do expect(response).to render_template :edit - expect(assigns(:settings).errors.count).to be 6 + expect(assigns(:settings).errors.count).to be 7 expect(Spree::Config.account_invoices_monthly_fixed).to eq 5 expect(Spree::Config.account_invoices_monthly_rate).to eq 0.02 expect(Spree::Config.account_invoices_monthly_cap).to eq 50 expect(Spree::Config.account_invoices_tax_rate).to eq 0.1 expect(Spree::Config.shop_trial_length_days).to eq 30 + expect(Spree::Config.minimum_billable_turnover).to eq -1 end end @@ -76,6 +79,7 @@ describe Admin::BusinessModelConfigurationController, type: :controller do params[:settings][:account_invoices_monthly_cap] = '30' params[:settings][:account_invoices_tax_rate] = '0.15' params[:settings][:shop_trial_length_days] = '20' + params[:settings][:minimum_billable_turnover] = '0' end it "sets global config to the specified values" do @@ -86,6 +90,7 @@ describe Admin::BusinessModelConfigurationController, type: :controller do expect(Spree::Config.account_invoices_monthly_cap).to eq 30 expect(Spree::Config.account_invoices_tax_rate).to eq 0.15 expect(Spree::Config.shop_trial_length_days).to eq 20 + expect(Spree::Config.minimum_billable_turnover).to eq 0 end end end From cbd0ace0981db928f16d3bd474842e266f0ed35a Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Thu, 24 Mar 2016 16:57:56 +0000 Subject: [PATCH 215/215] Code tidying and currency symbol internationalisation --- .../edit.html.haml | 6 ++++-- lib/open_food_network/bill_calculator.rb | 21 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/views/admin/business_model_configuration/edit.html.haml b/app/views/admin/business_model_configuration/edit.html.haml index 8a5c368444..a38e22eb17 100644 --- a/app/views/admin/business_model_configuration/edit.html.haml +++ b/app/views/admin/business_model_configuration/edit.html.haml @@ -92,10 +92,12 @@ = label_tag :included_tax, t(:included_tax) %span.icon-question-sign{'ofn-with-tip' => "The total tax included in the example monthly bill, given the settings and the turnover provided."} .two.columns.omega - %input.fullwidth{ id: 'included_tax', type: "text", readonly: true, ng: { value: 'includedTax() | currency' } } + %span= Spree::Money.currency_symbol + %input.fullwidth{ id: 'included_tax', type: "text", readonly: true, ng: { value: 'includedTax()' } } .row .three.columns.alpha = label_tag :total_incl_tax, t(:total_monthly_bill_incl_tax) %span.icon-question-sign{'ofn-with-tip' => "The example total monthly bill with tax included, given the settings and the turnover provided."} .two.columns.omega - %input.fullwidth{ id: 'total_incl_tax', type: "text", readonly: true, ng: { value: 'total() | currency' } } + %span= Spree::Money.currency_symbol + %input.fullwidth{ id: 'total_incl_tax', type: "text", readonly: true, ng: { value: 'total()' } } diff --git a/lib/open_food_network/bill_calculator.rb b/lib/open_food_network/bill_calculator.rb index d5302c0996..207a799bda 100644 --- a/lib/open_food_network/bill_calculator.rb +++ b/lib/open_food_network/bill_calculator.rb @@ -1,21 +1,24 @@ -module OpenFoodNetwork + module OpenFoodNetwork class BillCalculator attr_accessor :turnover, :fixed, :rate, :cap, :tax_rate, :min_bill_to def initialize(opts={}) - @turnover = opts[:turnover] || 0 - @fixed = opts[:fixed] || Spree::Config[:account_invoices_monthly_fixed] - @rate = opts[:rate] || Spree::Config[:account_invoices_monthly_rate] - @cap = opts[:cap] || Spree::Config[:account_invoices_monthly_cap] - @tax_rate = opts[:tax_rate] || Spree::Config[:account_invoices_tax_rate] - @min_bill_to = opts[:min_bill_to] || Spree::Config[:minimum_billable_turnover] + defaults = { + fixed: :account_invoices_monthly_fixed + rate: :account_invoices_monthly_rate + cap: :account_invoices_monthly_cap + tax_rate: :account_invoices_tax_rate + min_bill_to: :minimum_billable_turnover + } + defaults.each do |key, config| + this[key] = opts[key] || Spree::Config[config] + end end def bill bill = fixed + (turnover * rate) - bill = cap > 0 ? [bill, cap].min : bill + bill = [bill, cap].min if cap > 0 bill = turnover > min_bill_to ? bill : 0 bill * (1 + tax_rate) end - end end