From 4414a3f28755ca8cff89c44f13eb8212218cfdca Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 9 Apr 2016 10:05:45 +0100 Subject: [PATCH 001/101] Fix spelling mistake --- app/models/spree/user_decorator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 17473fd521..10a1c28972 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -55,7 +55,7 @@ Spree.user_class.class_eval do end # Returns orders and their associated payments for all distributors that have been ordered from - def compelete_orders_by_distributor + def complete_orders_by_distributor Enterprise .includes(distributed_orders: { payments: :payment_method }) .where(enterprises: { id: enterprises_ordered_from }, @@ -65,7 +65,7 @@ Spree.user_class.class_eval do def orders_by_distributor # Remove uncompleted payments as these will not be reflected in order balance - data_array = compelete_orders_by_distributor.to_a + data_array = complete_orders_by_distributor.to_a remove_uncompleted_payments(data_array) data_array.sort! { |a, b| b.distributed_orders.length <=> a.distributed_orders.length } end From bc048a943c776f910ff6cabe44df773d65b59e53 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 10 Apr 2016 22:23:39 +0100 Subject: [PATCH 002/101] Show all payments, format unsuccessful payments grey, add 'invalid' translation. --- .../stylesheets/darkswarm/account.css.sass | 7 +++++ app/models/spree/user_decorator.rb | 6 ++-- app/serializers/api/payment_serializer.rb | 2 +- app/views/spree/users/_fat.html.haml | 6 ++-- config/locales/en-GB.yml | 31 +++++++++++++++++++ config/locales/en.yml | 1 + 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/account.css.sass b/app/assets/stylesheets/darkswarm/account.css.sass index f582545b74..0f3de5af57 100644 --- a/app/assets/stylesheets/darkswarm/account.css.sass +++ b/app/assets/stylesheets/darkswarm/account.css.sass @@ -32,6 +32,13 @@ .debit color: $clr-brick + .invalid + color: $ofn-grey + .credit + color: $ofn-grey + .debit + color: $ofn-grey + .distributor-balance.paid visibility: hidden diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 10a1c28972..274330d1da 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -66,7 +66,7 @@ Spree.user_class.class_eval do def orders_by_distributor # Remove uncompleted payments as these will not be reflected in order balance data_array = complete_orders_by_distributor.to_a - remove_uncompleted_payments(data_array) + remove_payments_in_checkout(data_array) data_array.sort! { |a, b| b.distributed_orders.length <=> a.distributed_orders.length } end @@ -78,10 +78,10 @@ Spree.user_class.class_eval do end end - def remove_uncompleted_payments(enterprises) + def remove_payments_in_checkout(enterprises) enterprises.each do |enterprise| enterprise.distributed_orders.each do |order| - order.payments.keep_if { |payment| payment.state == "completed" } + order.payments.keep_if { |payment| payment.state != "checkout" } end end end diff --git a/app/serializers/api/payment_serializer.rb b/app/serializers/api/payment_serializer.rb index d48f75a07f..8465998db6 100644 --- a/app/serializers/api/payment_serializer.rb +++ b/app/serializers/api/payment_serializer.rb @@ -1,6 +1,6 @@ module Api class PaymentSerializer < ActiveModel::Serializer - attributes :amount, :updated_at, :payment_method + attributes :amount, :updated_at, :payment_method, :state def payment_method object.payment_method.name end diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index c10cf8254a..9f9ad67b10 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -19,10 +19,10 @@ %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"} - %td.order1= t :payment + %tr.payment-row{"ng-repeat" => "payment in order.payments", "ng-class" => "{'invalid': payment.state != 'completed'}"} + %td.order1{"bo-text" => "payment.payment_method"} %td.order2{"bo-text" => "payment.updated_at"} - %td.order3.show-for-large-up{"bo-text" => "payment.payment_method"} + %td.order3.show-for-large-up{"bo-text" => "'spree.payment_states.' + payment.state | t | capitalize"} %td.order4.show-for-large-up %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 diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 7301ef4f2c..3e793f88a4 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -635,3 +635,34 @@ 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" + 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 + invalid: invalid + 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/config/locales/en.yml b/config/locales/en.yml index 5bf35d1aa5..020011ea8d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -996,6 +996,7 @@ Please follow the instructions there to make your enterprise visible on the Open pending: pending processing: processing void: void + invalid: invalid order_state: address: address adjustments: adjustments From 14830237692bb005c409a83fde4df9caace58a0d Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 10 Apr 2016 22:48:38 +0100 Subject: [PATCH 003/101] Add a sweet warning sign --- app/views/spree/users/_fat.html.haml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index 9f9ad67b10..df5bd71c5d 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -22,7 +22,9 @@ %tr.payment-row{"ng-repeat" => "payment in order.payments", "ng-class" => "{'invalid': payment.state != 'completed'}"} %td.order1{"bo-text" => "payment.payment_method"} %td.order2{"bo-text" => "payment.updated_at"} - %td.order3.show-for-large-up{"bo-text" => "'spree.payment_states.' + payment.state | t | capitalize"} + %td.order3.show-for-large-up + %i{"ng-class" => "{'ofn-i_012-warning': payment.state == 'invalid' || payment.state == 'void'}"} + %span{"bo-text" => "'spree.payment_states.' + payment.state | t | capitalize"} %td.order4.show-for-large-up %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 From fc719230a3ea29a4b9fd16c5bb4a075c323aa0e0 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Fri, 15 Apr 2016 08:26:12 +0100 Subject: [PATCH 004/101] Add failed payments, update spec --- app/views/spree/users/_fat.html.haml | 2 +- spec/models/spree/user_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/spree/users/_fat.html.haml b/app/views/spree/users/_fat.html.haml index df5bd71c5d..5c87077b4c 100644 --- a/app/views/spree/users/_fat.html.haml +++ b/app/views/spree/users/_fat.html.haml @@ -23,7 +23,7 @@ %td.order1{"bo-text" => "payment.payment_method"} %td.order2{"bo-text" => "payment.updated_at"} %td.order3.show-for-large-up - %i{"ng-class" => "{'ofn-i_012-warning': payment.state == 'invalid' || payment.state == 'void'}"} + %i{"ng-class" => "{'ofn-i_012-warning': payment.state == 'invalid' || payment.state == 'void' || payment.state == 'failed'}"} %span{"bo-text" => "'spree.payment_states.' + payment.state | t | capitalize"} %td.order4.show-for-large-up %td.order5.text-right{"ng-class" => "{'credit' : payment.amount > 0, 'debit' : payment.amount < 0, 'paid' : payment.amount == 0}","bo-text" => "payment.amount | localizeCurrency"} diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index ee01326316..9911c16a51 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -92,7 +92,7 @@ describe Spree.user_class do 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!(:payment) { create(:payment, order: d1o2, state: 'checkout')} it "returns enterprises that the user has ordered from" do expect(u1.enterprises_ordered_from).to eq [distributor1.id] @@ -114,7 +114,7 @@ describe Spree.user_class do expect(u1.orders_by_distributor.first.distributed_orders).not_to include d1o3 end - it "doesn't return uncompleted payments" do + it "doesn't return payments that are still at checkout stage" do expect(u1.orders_by_distributor.first.distributed_orders.map{|o| o.payments}.flatten).not_to include payment end end From f986c5898e5d0603a5166dd8dc21b30f13681226 Mon Sep 17 00:00:00 2001 From: Lynne Date: Sat, 16 Apr 2016 03:23:26 +0100 Subject: [PATCH 005/101] Updating en-GB file to reflect recent additions (#909) --- 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 9e321a63c067f41541bd7a04c83981a1209d8d41 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 2 Mar 2016 14:56:25 +1100 Subject: [PATCH 006/101] Don't put master in order cycle - we don't do that no more --- spec/features/consumer/shopping/cart_spec.rb | 2 +- spec/features/consumer/shopping/checkout_spec.rb | 3 ++- spec/features/consumer/shopping/shopping_spec.rb | 4 ++-- spec/support/request/shop_workflow.rb | 11 +++-------- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/spec/features/consumer/shopping/cart_spec.rb b/spec/features/consumer/shopping/cart_spec.rb index 58c570735d..a80fd908ff 100644 --- a/spec/features/consumer/shopping/cart_spec.rb +++ b/spec/features/consumer/shopping/cart_spec.rb @@ -11,7 +11,7 @@ feature "full-page cart", js: true do let!(:zone) { create(:zone_with_member) } let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } let(:supplier) { create(:supplier_enterprise) } - let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.master]) } + let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } let(:enterprise_fee) { create(:enterprise_fee, amount: 11.00, tax_category: product.tax_category) } let(:product) { create(:taxed_product, supplier: supplier, zone: zone, price: 110.00, tax_rate_amount: 0.1) } let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 74145bbe1a..b1c8856f8d 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -11,9 +11,10 @@ feature "As a consumer I want to check out my cart", js: true do let!(:zone) { create(:zone_with_member) } let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:supplier) { create(:supplier_enterprise) } - let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.master]) } + let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [variant]) } let(:enterprise_fee) { create(:enterprise_fee, amount: 1.23, tax_category: product.tax_category) } let(:product) { create(:taxed_product, supplier: supplier, price: 10, zone: zone, tax_rate_amount: 0.1) } + let(:variant) { product.variants.first } let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } before do diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index a8545af3db..8fa78f6006 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -182,7 +182,7 @@ feature "As a consumer I want to shop with a distributor", js: true do describe "with variants on the product" do let(:variant) { create(:variant, product: product, on_hand: 10 ) } before do - add_product_and_variant_to_order_cycle(exchange, product, variant) + add_variant_to_order_cycle(exchange, variant) set_order_cycle(order, oc1) visit shop_path end @@ -217,7 +217,7 @@ feature "As a consumer I want to shop with a distributor", js: true do let(:variant) { create(:variant, product: product) } before do - add_product_and_variant_to_order_cycle(exchange, product, variant) + add_variant_to_order_cycle(exchange, variant) set_order_cycle(order, oc1) visit shop_path end diff --git a/spec/support/request/shop_workflow.rb b/spec/support/request/shop_workflow.rb index a0e6ab846c..279ead2630 100644 --- a/spec/support/request/shop_workflow.rb +++ b/spec/support/request/shop_workflow.rb @@ -18,7 +18,7 @@ module ShopWorkflow def add_product_to_cart populator = Spree::OrderPopulator.new(order, order.currency) - populator.populate(variants: {product.master.id => 1}) + populator.populate(variants: {product.variants.first.id => 1}) # Recalculate fee totals order.update_distribution_charge! @@ -28,15 +28,10 @@ module ShopWorkflow find("dd a", text: name).trigger "click" end - def add_product_to_order_cycle(exchange, product) - exchange.variants << product.master + def add_variant_to_order_cycle(exchange, variant) + exchange.variants << variant end - def add_product_and_variant_to_order_cycle(exchange, product, variant) - exchange.variants << product.master - exchange.variants << variant - end - def set_order_cycle(order, order_cycle) order.update_attribute(:order_cycle, order_cycle) end From 243f59c87d9a645ef58e7e1b59f247663b96dffd Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 2 Mar 2016 15:23:38 +1100 Subject: [PATCH 007/101] When there's an out of stock product in the cart, checkout returns user to cart --- app/controllers/checkout_controller.rb | 2 +- spec/features/consumer/shopping/checkout_spec.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 1cffc05734..3ac826df52 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -152,7 +152,7 @@ class CheckoutController < Spree::CheckoutController # Overriding Spree's methods def raise_insufficient_quantity flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) - redirect_to main_app.shop_path + redirect_to cart_path end def redirect_to_paypal_express_form_if_needed diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index b1c8856f8d..2cd959a1b4 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -46,6 +46,22 @@ feature "As a consumer I want to check out my cart", js: true do distributor.shipping_methods << sm3 end + describe "when I have an out of stock product in my cart" do + before do + Spree::Config.set allow_backorders: false + variant.on_hand = 0 + variant.save! + end + + it "returns me to the cart with an error message" do + visit checkout_path + + page.should_not have_selector 'closing', text: "Checkout now" + page.should have_selector 'closing', text: "Your shopping cart" + page.should have_content "An item in your cart has become unavailable" + end + end + context "on the checkout page" do before do visit checkout_path From d45b5254971050093cd20348da195834a0c65661 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 9 Mar 2016 15:33:37 +1100 Subject: [PATCH 008/101] When there's an out of stock product in the cart, placing order returns user to the cart --- .../darkswarm/services/checkout.js.coffee | 9 ++-- app/controllers/checkout_controller.rb | 13 +++++- .../consumer/shopping/checkout_spec.rb | 12 ++++++ .../services/checkout_spec.js.coffee | 41 +++++++++++-------- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/checkout.js.coffee b/app/assets/javascripts/darkswarm/services/checkout.js.coffee index 652ecd02f9..34cae22fcc 100644 --- a/app/assets/javascripts/darkswarm/services/checkout.js.coffee +++ b/app/assets/javascripts/darkswarm/services/checkout.js.coffee @@ -10,9 +10,12 @@ Darkswarm.factory 'Checkout', (CurrentOrder, ShippingMethods, PaymentMethods, $h $http.put('/checkout', {order: @preprocess()}).success (data, status)=> Navigation.go data.path .error (response, status)=> - Loading.clear() - @errors = response.errors - RailsFlashLoader.loadFlash(response.flash) + if response.path + Navigation.go response.path + else + Loading.clear() + @errors = response.errors + RailsFlashLoader.loadFlash(response.flash) # Rails wants our Spree::Address data to be provided with _attributes preprocess: -> diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 3ac826df52..7b34bbcff5 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -151,8 +151,17 @@ class CheckoutController < Spree::CheckoutController # Overriding Spree's methods def raise_insufficient_quantity - flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) - redirect_to cart_path + respond_to do |format| + format.html do + flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) + redirect_to cart_path + end + + format.json do + flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) + render json: {path: cart_path}, status: 400 + end + end end def redirect_to_paypal_express_form_if_needed diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 2cd959a1b4..9e9d830a50 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -230,6 +230,18 @@ feature "As a consumer I want to check out my cart", js: true do page.should have_content "Your order has been processed successfully" end + it "takes us to the cart page with an error when a product becomes out of stock just before we purchase", js: true do + Spree::Config.set allow_backorders: false + variant.on_hand = 0 + variant.save! + + place_order + + page.should_not have_content "Your order has been processed successfully" + page.should have_selector 'closing', text: "Your shopping cart" + page.should have_content "Out of Stock" + end + context "when we are charged a shipping fee" do before { choose sm2.name } diff --git a/spec/javascripts/unit/darkswarm/services/checkout_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/checkout_spec.js.coffee index e3ba3f51de..718504a377 100644 --- a/spec/javascripts/unit/darkswarm/services/checkout_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/checkout_spec.js.coffee @@ -5,7 +5,7 @@ describe 'Checkout service', -> Navigation = null flash = null scope = null - FlashLoaderMock = + FlashLoaderMock = loadFlash: (arg)-> paymentMethods = [{ id: 99 @@ -41,10 +41,10 @@ describe 'Checkout service', -> module 'Darkswarm' module ($provide)-> - $provide.value "RailsFlashLoader", FlashLoaderMock - $provide.value "currentOrder", orderData - $provide.value "shippingMethods", shippingMethods - $provide.value "paymentMethods", paymentMethods + $provide.value "RailsFlashLoader", FlashLoaderMock + $provide.value "currentOrder", orderData + $provide.value "shippingMethods", shippingMethods + $provide.value "paymentMethods", paymentMethods null inject ($injector, _$httpBackend_, $rootScope)-> @@ -80,26 +80,33 @@ describe 'Checkout service', -> it 'Gets the current payment method', -> expect(Checkout.paymentMethod()).toEqual null Checkout.order.payment_method_id = 99 - expect(Checkout.paymentMethod()).toEqual paymentMethods[0] + expect(Checkout.paymentMethod()).toEqual paymentMethods[0] it "Posts the Checkout to the server", -> $httpBackend.expectPUT("/checkout", {order: Checkout.preprocess()}).respond 200, {path: "test"} Checkout.submit() $httpBackend.flush() - it "sends flash messages to the flash service", -> - spyOn(FlashLoaderMock, "loadFlash") # Stubbing out writes to window.location - $httpBackend.expectPUT("/checkout").respond 400, {flash: {error: "frogs"}} - Checkout.submit() + describe "when there is an error", -> + it "redirects when a redirect is given", -> + $httpBackend.expectPUT("/checkout").respond 400, {path: 'path'} + Checkout.submit() + $httpBackend.flush() + expect(Navigation.go).toHaveBeenCalledWith 'path' - $httpBackend.flush() - expect(FlashLoaderMock.loadFlash).toHaveBeenCalledWith {error: "frogs"} + it "sends flash messages to the flash service", -> + spyOn(FlashLoaderMock, "loadFlash") # Stubbing out writes to window.location + $httpBackend.expectPUT("/checkout").respond 400, {flash: {error: "frogs"}} + Checkout.submit() - it "puts errors into the scope", -> - $httpBackend.expectPUT("/checkout").respond 400, {errors: {error: "frogs"}} - Checkout.submit() - $httpBackend.flush() - expect(Checkout.errors).toEqual {error: "frogs"} + $httpBackend.flush() + expect(FlashLoaderMock.loadFlash).toHaveBeenCalledWith {error: "frogs"} + + it "puts errors into the scope", -> + $httpBackend.expectPUT("/checkout").respond 400, {errors: {error: "frogs"}} + Checkout.submit() + $httpBackend.flush() + expect(Checkout.errors).toEqual {error: "frogs"} describe "data preprocessing", -> beforeEach -> From 17f69bd182e61590fbef23871c1e0e18141af13a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 24 Mar 2016 11:56:26 +1100 Subject: [PATCH 009/101] Remove trailing whitespace --- .../templates/shop_variant.html.haml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/templates/shop_variant.html.haml b/app/assets/javascripts/templates/shop_variant.html.haml index 00e4f70d7c..83463da328 100644 --- a/app/assets/javascripts/templates/shop_variant.html.haml +++ b/app/assets/javascripts/templates/shop_variant.html.haml @@ -1,6 +1,6 @@ .variants.row .small-12.medium-4.large-4.columns.variant-name - .table-cell + .table-cell .inline {{ variant.name_to_display }} .bulk-buy.inline{"bo-if" => "variant.product.group_buy"} %i.ofn-i_056-bulk>< @@ -10,25 +10,25 @@ -# WITHOUT GROUP BUY .small-5.medium-3.large-3.columns.text-right{"bo-if" => "!variant.product.group_buy"} - %input{type: :number, + %input{type: :number, integer: true, - value: nil, - min: 0, + value: nil, + min: 0, placeholder: "0", "ofn-disable-scroll" => true, "ng-model" => "variant.line_item.quantity", max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} - + -# WITH GROUP BUY .small-5.medium-3.large-3.columns.text-right{"bo-if" => "variant.product.group_buy"} %span.bulk-input-container %span.bulk-input - %input.bulk.first{type: :number, - value: nil, + %input.bulk.first{type: :number, + value: nil, integer: true, - min: 0, + min: 0, "ng-model" => "variant.line_item.quantity", placeholder: "{{'shop_variant_quantity_min' | t}}", "ofn-disable-scroll" => true, @@ -46,16 +46,16 @@ name: "variant_attributes[{{variant.id}}][max_quantity]"} .small-3.medium-1.large-1.columns.variant-unit - .table-cell + .table-cell %em {{ variant.unit_to_display }} .small-4.medium-2.large-2.columns.variant-price .table-cell.price - %i.ofn-i_009-close + %i.ofn-i_009-close {{ variant.price_with_fees | localizeCurrency }} -# Now in a template in app/assets/javascripts/templates ! - %price-breakdown{"price-breakdown" => "_", variant: "variant", + %price-breakdown{"price-breakdown" => "_", variant: "variant", "price-breakdown-append-to-body" => "true", "price-breakdown-placement" => "left", "price-breakdown-animation" => true} @@ -63,4 +63,4 @@ .small-12.medium-2.large-2.columns.total-price.text-right .table-cell %strong{"ng-class" => "{filled: variant.totalPrice()}"} - {{ variant.totalPrice() | localizeCurrency }} + {{ variant.totalPrice() | localizeCurrency }} From a1bcdc616f2d857720a25721d508afe38413ce06 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 24 Mar 2016 12:05:16 +1100 Subject: [PATCH 010/101] Extract add-to-cart inputs into partials --- .../shop_variant_no_group_buy.html.haml | 11 ++++++ .../shop_variant_with_group_buy.html.haml | 22 +++++++++++ .../templates/shop_variant.html.haml | 37 +------------------ 3 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml create mode 100644 app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml diff --git a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml new file mode 100644 index 0000000000..4ce584aa42 --- /dev/null +++ b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml @@ -0,0 +1,11 @@ +.small-5.medium-3.large-3.columns.text-right{"bo-if" => "!variant.product.group_buy"} + + %input{type: :number, + integer: true, + value: nil, + min: 0, + placeholder: "0", + "ofn-disable-scroll" => true, + "ng-model" => "variant.line_item.quantity", + max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", + name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} diff --git a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml new file mode 100644 index 0000000000..51f7599af7 --- /dev/null +++ b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml @@ -0,0 +1,22 @@ +.small-5.medium-3.large-3.columns.text-right{"bo-if" => "variant.product.group_buy"} + %span.bulk-input-container + %span.bulk-input + %input.bulk.first{type: :number, + value: nil, + integer: true, + min: 0, + "ng-model" => "variant.line_item.quantity", + placeholder: "{{'shop_variant_quantity_min' | t}}", + "ofn-disable-scroll" => true, + max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", + name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} + %span.bulk-input + %input.bulk.second{type: :number, + "ng-disabled" => "!variant.line_item.quantity", + integer: true, + min: 0, + "ng-model" => "variant.line_item.max_quantity", + placeholder: "{{'shop_variant_quantity_max' | t}}", + "ofn-disable-scroll" => true, + max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", + name: "variant_attributes[{{variant.id}}][max_quantity]"} diff --git a/app/assets/javascripts/templates/shop_variant.html.haml b/app/assets/javascripts/templates/shop_variant.html.haml index 83463da328..2c9eb932d5 100644 --- a/app/assets/javascripts/templates/shop_variant.html.haml +++ b/app/assets/javascripts/templates/shop_variant.html.haml @@ -7,44 +7,11 @@ %em>< \ {{'bulk' | t}} - -# WITHOUT GROUP BUY - .small-5.medium-3.large-3.columns.text-right{"bo-if" => "!variant.product.group_buy"} - %input{type: :number, - integer: true, - value: nil, - min: 0, - placeholder: "0", - "ofn-disable-scroll" => true, - "ng-model" => "variant.line_item.quantity", - max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", - name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} + %ng-include{src: "'partials/shop_variant_no_group_buy.html'"} + %ng-include{src: "'partials/shop_variant_with_group_buy.html'"} - -# WITH GROUP BUY - .small-5.medium-3.large-3.columns.text-right{"bo-if" => "variant.product.group_buy"} - %span.bulk-input-container - %span.bulk-input - %input.bulk.first{type: :number, - value: nil, - integer: true, - min: 0, - "ng-model" => "variant.line_item.quantity", - placeholder: "{{'shop_variant_quantity_min' | t}}", - "ofn-disable-scroll" => true, - max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", - name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} - %span.bulk-input - %input.bulk.second{type: :number, - "ng-disabled" => "!variant.line_item.quantity", - integer: true, - min: 0, - "ng-model" => "variant.line_item.max_quantity", - placeholder: "{{'shop_variant_quantity_max' | t}}", - "ofn-disable-scroll" => true, - max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", - name: "variant_attributes[{{variant.id}}][max_quantity]"} - .small-3.medium-1.large-1.columns.variant-unit .table-cell %em {{ variant.unit_to_display }} From 292d02749800620ce5ac71f4f7b24e8be1a08d93 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 24 Mar 2016 14:48:13 +1100 Subject: [PATCH 011/101] When adding an item to cart with not enough stock, add as much as we can without erroring --- app/models/spree/order_populator_decorator.rb | 25 ++++++-- spec/models/spree/order_populator_spec.rb | 60 +++++++++++++++++-- 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/app/models/spree/order_populator_decorator.rb b/app/models/spree/order_populator_decorator.rb index 1ca0f9efc2..7ff64cdf71 100644 --- a/app/models/spree/order_populator_decorator.rb +++ b/app/models/spree/order_populator_decorator.rb @@ -49,17 +49,32 @@ Spree::OrderPopulator.class_eval do def attempt_cart_add(variant_id, quantity, max_quantity = nil) quantity = quantity.to_i + max_quantity = max_quantity.to_i if max_quantity variant = Spree::Variant.find(variant_id) OpenFoodNetwork::ScopeVariantToHub.new(@distributor).scope(variant) - if quantity > 0 - if check_stock_levels(variant, quantity) && - check_order_cycle_provided_for(variant) && - check_variant_available_under_distribution(variant) - @order.add_variant(variant, quantity, max_quantity, currency) + if quantity > 0 && + check_order_cycle_provided_for(variant) && + check_variant_available_under_distribution(variant) + + quantity_to_add, max_quantity_to_add = quantities_to_add(variant, quantity, max_quantity) + + if quantity_to_add > 0 + @order.add_variant(variant, quantity_to_add, max_quantity_to_add, currency) end end end + def quantities_to_add(variant, quantity, max_quantity) + # If not enough stock is available, add as much as we can to the cart + on_hand = variant.on_hand + on_hand = [quantity, max_quantity].compact.max if Spree::Config.allow_backorders + quantity_to_add = [quantity, on_hand].min + max_quantity_to_add = [max_quantity, on_hand].min if max_quantity + + [quantity_to_add, max_quantity_to_add] + end + + def cart_remove(variant_id) variant = Spree::Variant.find(variant_id) @order.remove_variant(variant) diff --git a/spec/models/spree/order_populator_spec.rb b/spec/models/spree/order_populator_spec.rb index 7ba59fb47a..389c037b2e 100644 --- a/spec/models/spree/order_populator_spec.rb +++ b/spec/models/spree/order_populator_spec.rb @@ -149,13 +149,15 @@ module Spree end describe "attempt_cart_add" do - it "performs additional validations" do - variant = double(:variant) - quantity = 123 + let(:variant) { double(:variant, on_hand: 250) } + let(:quantity) { 123 } + + before do Spree::Variant.stub(:find).and_return(variant) VariantOverride.stub(:for).and_return(nil) + end - op.should_receive(:check_stock_levels).with(variant, quantity).and_return(true) + it "performs additional validations" do op.should_receive(:check_order_cycle_provided_for).with(variant).and_return(true) op.should_receive(:check_variant_available_under_distribution).with(variant). and_return(true) @@ -163,8 +165,58 @@ module Spree op.attempt_cart_add(333, quantity.to_s) end + + it "filters quantities through #quantities_to_add" do + op.should_receive(:quantities_to_add).with(variant, 123, 123). + and_return([5, 5]) + + op.stub(:check_order_cycle_provided_for) { true } + op.stub(:check_variant_available_under_distribution) { true } + + order.should_receive(:add_variant).with(variant, 5, 5, currency) + + op.attempt_cart_add(333, quantity.to_s, quantity.to_s) + end end + describe "quantities_to_add" do + let(:v) { double(:variant, on_hand: 10) } + context "when max_quantity is not provided" do + it "returns full amount when available" do + op.quantities_to_add(v, 5, nil).should == [5, nil] + end + + it "returns a limited amount when not entirely available" do + op.quantities_to_add(v, 15, nil).should == [10, nil] + end + end + + context "when max_quantity is provided" do + it "returns full amount when available" do + op.quantities_to_add(v, 5, 6).should == [5, 6] + end + + it "returns a limited amount when not entirely available" do + op.quantities_to_add(v, 15, 16).should == [10, 10] + end + end + + context "when backorders are allowed" do + around do |example| + Spree::Config.allow_backorders = true + example.run + Spree::Config.allow_backorders = false + end + + it "does not limit quantity" do + op.quantities_to_add(v, 15, nil).should == [15, nil] + end + + it "does not limit max_quantity" do + op.quantities_to_add(v, 15, 16).should == [15, 16] + end + end + end describe "validations" do describe "determining if distributor can supply products in cart" do From fee0f90a1b90251c7a8b6f969c35ed2eef3bb66f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 24 Mar 2016 15:48:35 +1100 Subject: [PATCH 012/101] After adding products to cart, return status of cart and available stock levels --- .../spree/orders_controller_decorator.rb | 18 ++++++++-- .../spree/orders_controller_spec.rb | 34 +++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index c6d325d0f8..4c2ab8cb63 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -30,19 +30,33 @@ Spree::OrdersController.class_eval do Spree::Adjustment.without_callbacks do populator = Spree::OrderPopulator.new(current_order(true), current_currency) + if populator.populate(params.slice(:products, :variants, :quantity), true) fire_event('spree.cart.add') fire_event('spree.order.contents_changed') current_order.update! - render json: true, status: 200 + render json: {error: false, stock_levels: stock_levels}, status: 200 + else - render json: false, status: 402 + render json: {error: true}, status: 412 end end end + def stock_levels + Hash[ + current_order.line_items.map do |li| + [li.variant.id, + {quantity: li.quantity, + max_quantity: li.max_quantity, + on_hand: li.variant.on_hand}] + end + ] + end + + def update_distribution @order = current_order(true) diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index 73c72f3386..756be1d3ec 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -42,6 +42,36 @@ describe Spree::OrdersController do flash[:info].should == "The hub you have selected is temporarily closed for orders. Please try again later." end + describe "returning stock levels in JSON on success" do + let(:product) { create(:simple_product) } + + it "returns stock levels as JSON" do + controller.stub(:stock_levels) { 'my_stock_levels' } + Spree::OrderPopulator.stub(:new).and_return(populator = double()) + populator.stub(:populate) { true } + + xhr :post, :populate, use_route: :spree, format: :json + + data = JSON.parse(response.body) + data['stock_levels'].should == 'my_stock_levels' + end + + describe "generating stock levels" do + let!(:order) { create(:order) } + let!(:li) { create(:line_item, order: order, variant: v, quantity: 2, max_quantity: 3) } + let!(:v) { create(:variant, count_on_hand: 4) } + + before do + order.reload + controller.stub(:current_order) { order } + end + + it "returns a hash with variant id, quantity, max_quantity and stock on hand" do + controller.stock_levels.should == {v.id => {quantity: 2, max_quantity: 3, on_hand: 4}} + end + end + end + context "adding a group buy product to the cart" do it "sets a variant attribute for the max quantity" do distributor_product = create(:distributor_enterprise) @@ -68,7 +98,7 @@ describe Spree::OrdersController do Spree::OrderPopulator.stub(:new).and_return(populator = double()) populator.stub(:populate).and_return false xhr :post, :populate, use_route: :spree, format: :json - response.status.should == 402 + response.status.should == 412 end it "tells populator to overwrite" do @@ -78,7 +108,7 @@ describe Spree::OrdersController do end end - context "removing line items from cart" do + describe "removing line items from cart" do describe "when I pass params that includes a line item no longer in our cart" do it "should silently ignore the missing line item" do order = subject.current_order(true) From 8a62d26af4728bd709d174a443be143170321b8d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 24 Mar 2016 16:24:36 +1100 Subject: [PATCH 013/101] After adding an item to the cart, when out of stock, remove from cart and reset client-side stock level --- .../darkswarm/services/cart.js.coffee | 11 ++++++++++ .../darkswarm/services/cart_spec.js.coffee | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index f1c9421a15..c9fdd1471b 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -31,12 +31,23 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, storage)-> $http.post('/orders/populate', @data()).success (data, status)=> @saved() @update_running = false + + @compareAndNotifyStockLevels data.stock_levels + @popQueue() if @update_enqueued .error (response, status)=> @scheduleRetry(status) @update_running = false + compareAndNotifyStockLevels: (stockLevels) => + for li in @line_items_present() + if !stockLevels[li.variant.id]? + alert "Variant out of stock: #{li.variant.id}" + li.quantity = 0 + li.max_quantity = 0 if li.max_quantity? + li.variant.count_on_hand = 0 + popQueue: => @update_enqueued = false @scheduleUpdate() diff --git a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee index 518a524010..41cee8de4b 100644 --- a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee @@ -126,6 +126,27 @@ describe 'Cart service', -> $httpBackend.flush() expect(Cart.scheduleRetry).toHaveBeenCalled() + describe "verifying stock levels after update", -> + describe "when an item is out of stock", -> + it "reduces the quantity in the cart", -> + li = {variant: {id: 1}, quantity: 5} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels({}) + expect(li.quantity).toEqual 0 + expect(li.max_quantity).toBeUndefined() + + it "reduces the max_quantity in the cart", -> + li = {variant: {id: 1}, quantity: 5, max_quantity: 6} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels({}) + expect(li.max_quantity).toEqual 0 + + it "resets the count on hand available", -> + li = {variant: {id: 1, count_on_hand: 10}, quantity: 5} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels({}) + expect(li.variant.count_on_hand).toEqual 0 + it "pops the queue", -> Cart.update_enqueued = true spyOn(Cart, 'scheduleUpdate') From 6fbbe580c5bfc35c2455bb8ca19c834628d8efaf Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 24 Mar 2016 16:41:54 +1100 Subject: [PATCH 014/101] After adding an item to the cart, when less quantity available, reduce quantity and reset client-side stock level --- .../darkswarm/services/cart.js.coffee | 8 +++++ .../darkswarm/services/cart_spec.js.coffee | 29 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index c9fdd1471b..973170e414 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -47,6 +47,14 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, storage)-> li.quantity = 0 li.max_quantity = 0 if li.max_quantity? li.variant.count_on_hand = 0 + else + if stockLevels[li.variant.id].quantity < li.quantity + alert "Variant quantity reduced: #{li.variant.id}" + li.quantity = stockLevels[li.variant.id].quantity + if stockLevels[li.variant.id].max_quantity < li.max_quantity + alert "Variant max_quantity reduced: #{li.variant.id}" + li.max_quantity = stockLevels[li.variant.id].max_quantity + li.variant.count_on_hand = stockLevels[li.variant.id].on_hand popQueue: => @update_enqueued = false diff --git a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee index 41cee8de4b..58b7795124 100644 --- a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee @@ -131,22 +131,45 @@ describe 'Cart service', -> it "reduces the quantity in the cart", -> li = {variant: {id: 1}, quantity: 5} spyOn(Cart, 'line_items_present').andReturn [li] - Cart.compareAndNotifyStockLevels({}) + Cart.compareAndNotifyStockLevels {} expect(li.quantity).toEqual 0 expect(li.max_quantity).toBeUndefined() it "reduces the max_quantity in the cart", -> li = {variant: {id: 1}, quantity: 5, max_quantity: 6} spyOn(Cart, 'line_items_present').andReturn [li] - Cart.compareAndNotifyStockLevels({}) + Cart.compareAndNotifyStockLevels {} expect(li.max_quantity).toEqual 0 it "resets the count on hand available", -> li = {variant: {id: 1, count_on_hand: 10}, quantity: 5} spyOn(Cart, 'line_items_present').andReturn [li] - Cart.compareAndNotifyStockLevels({}) + Cart.compareAndNotifyStockLevels {} expect(li.variant.count_on_hand).toEqual 0 + describe "when the quantity available is less than that requested", -> + it "reduces the quantity in the cart", -> + li = {variant: {id: 1}, quantity: 6} + stockLevels = {1: {quantity: 5, on_hand: 5}} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels stockLevels + expect(li.quantity).toEqual 5 + expect(li.max_quantity).toBeUndefined() + + it "reduces the max_quantity in the cart", -> + li = {variant: {id: 1}, quantity: 6, max_quantity: 7} + stockLevels = {1: {quantity: 5, max_quantity: 5, on_hand: 5}} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels stockLevels + expect(li.max_quantity).toEqual 5 + + it "resets the count on hand available", -> + li = {variant: {id: 1}, quantity: 6} + stockLevels = {1: {quantity: 5, on_hand: 6}} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels stockLevels + expect(li.variant.count_on_hand).toEqual 6 + it "pops the queue", -> Cart.update_enqueued = true spyOn(Cart, 'scheduleUpdate') From 792e17c385cd3cc3a3d6d3d2c0fb22983df1ea7d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 8 Apr 2016 11:17:54 +1000 Subject: [PATCH 015/101] When removing variant from order, if not found then do nothing --- app/models/spree/order_decorator.rb | 2 +- spec/models/spree/order_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index f0e9324185..53a1b81873 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -99,7 +99,7 @@ Spree::Order.class_eval do def remove_variant(variant) line_items(:reload) current_item = find_line_item_by_variant(variant) - current_item.destroy + current_item.andand.destroy end diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index ab86be1580..4db0541d6c 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -366,6 +366,7 @@ describe Spree::Order do let(:order) { create(:order) } let(:v1) { create(:variant) } let(:v2) { create(:variant) } + let(:v3) { create(:variant) } before do order.add_variant v1 @@ -376,6 +377,12 @@ describe Spree::Order do order.remove_variant v1 order.line_items(:reload).map(&:variant).should == [v2] end + + it "does nothing when there is no matching line item" do + expect do + order.remove_variant v3 + end.to change(order.line_items(:reload), :count).by(0) + end end describe "emptying the order" do From 8695dea0a5c36fd4c6753e89d0c93b5a652dacba Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 8 Apr 2016 11:19:34 +1000 Subject: [PATCH 016/101] Remove variant from cart when it becomes out of stock --- app/models/spree/order_populator_decorator.rb | 2 ++ spec/models/spree/order_populator_spec.rb | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/app/models/spree/order_populator_decorator.rb b/app/models/spree/order_populator_decorator.rb index 7ff64cdf71..82d9aa85e7 100644 --- a/app/models/spree/order_populator_decorator.rb +++ b/app/models/spree/order_populator_decorator.rb @@ -60,6 +60,8 @@ Spree::OrderPopulator.class_eval do if quantity_to_add > 0 @order.add_variant(variant, quantity_to_add, max_quantity_to_add, currency) + else + @order.remove_variant variant end end end diff --git a/spec/models/spree/order_populator_spec.rb b/spec/models/spree/order_populator_spec.rb index 389c037b2e..9f7cc47c51 100644 --- a/spec/models/spree/order_populator_spec.rb +++ b/spec/models/spree/order_populator_spec.rb @@ -177,6 +177,19 @@ module Spree op.attempt_cart_add(333, quantity.to_s, quantity.to_s) end + + it "removes variants which have become out of stock" do + op.should_receive(:quantities_to_add).with(variant, 123, 123). + and_return([0, 0]) + + op.stub(:check_order_cycle_provided_for) { true } + op.stub(:check_variant_available_under_distribution) { true } + + order.should_receive(:remove_variant).with(variant) + order.should_receive(:add_variant).never + + op.attempt_cart_add(333, quantity.to_s, quantity.to_s) + end end describe "quantities_to_add" do From cfe062918b3580c20a65a13b3c7c7c2d50a726f6 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 8 Apr 2016 11:22:06 +1000 Subject: [PATCH 017/101] When a variant goes out of stock, disable the input and grey out the row --- .../shop_variant_no_group_buy.html.haml | 1 + .../darkswarm/_shop-product-rows.css.sass | 28 ++++++++-------- app/views/shop/products/_form.html.haml | 4 +-- .../consumer/shopping/shopping_spec.rb | 33 +++++++++++++++++++ 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml index 4ce584aa42..36a4e44ade 100644 --- a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml +++ b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml @@ -8,4 +8,5 @@ "ofn-disable-scroll" => true, "ng-model" => "variant.line_item.quantity", max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", + "ng-disabled" => "!variant.on_demand && variant.count_on_hand == 0", name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} diff --git a/app/assets/stylesheets/darkswarm/_shop-product-rows.css.sass b/app/assets/stylesheets/darkswarm/_shop-product-rows.css.sass index c67b030292..c0e4fb27aa 100644 --- a/app/assets/stylesheets/darkswarm/_shop-product-rows.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-product-rows.css.sass @@ -11,20 +11,20 @@ padding-bottom: 0em display: table line-height: 1.1 - // outline: 1px solid red + // outline: 1px solid red - @media all and (max-width: 768px) - font-size: 0.875rem + @media all and (max-width: 768px) + font-size: 0.875rem + + @media all and (max-width: 640px) + font-size: 0.75rem - @media all and (max-width: 640px) - font-size: 0.75rem - .table-cell display: table-cell vertical-align: middle height: 37px - // ROW VARIANTS + // ROW VARIANTS .row.variants margin-left: 0 margin-right: 0 @@ -35,7 +35,10 @@ background-color: #f9f9f9 &:hover, &:focus, &:active background-color: $clr-brick-ultra-light - + + &.out-of-stock + opacity: 0.2 + // Variant name .variant-name padding-left: 7.9375rem @@ -52,7 +55,7 @@ height: 27px // Variant unit - .variant-unit + .variant-unit padding-left: 0rem padding-right: 0rem color: #888 @@ -88,18 +91,18 @@ margin-left: 0 margin-right: 0 background: #fff - + .columns padding-top: 1em padding-bottom: 1em line-height: 1 - + @media all and (max-width: 768px) padding-top: 0.65rem padding-bottom: 0.65rem .summary-header - padding-left: 7.9375rem + padding-left: 7.9375rem @media all and (max-width: 768px) padding-left: 4.9375rem @media all and (max-width: 640px) @@ -118,4 +121,3 @@ color: $clr-brick i font-size: 0.8em - diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index 18868e5c77..f2be498451 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -32,9 +32,9 @@ %div.pad-top{bindonce: true} %product.animate-repeat{"ng-controller" => "ProductNodeCtrl", "ng-repeat" => "product in filteredProducts = (Products.products | products:query | taxons:activeTaxons | properties: activeProperties) track by product.id ", "id" => "product-{{ product.id }}"} - = render partial: "shop/products/summary" + = render "shop/products/summary" %shop-variant{variant: 'product.master', "bo-if" => "!product.hasVariants", "id" => "variant-{{ product.master.id }}"} - %shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants track by variant.id", "id" => "variant-{{ variant.id }}"} + %shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.count_on_hand == 0}"} %product{"ng-show" => "Products.loading"} .row.summary diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 8fa78f6006..ab94c678d7 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -215,9 +215,11 @@ feature "As a consumer I want to shop with a distributor", js: true do let(:exchange) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } let(:product) { create(:simple_product) } let(:variant) { create(:variant, product: product) } + let(:variant2) { create(:variant, product: product) } before do add_variant_to_order_cycle(exchange, variant) + add_variant_to_order_cycle(exchange, variant2) set_order_cycle(order, oc1) visit shop_path end @@ -235,6 +237,37 @@ feature "As a consumer I want to shop with a distributor", js: true do Spree::LineItem.where(id: li).should be_empty end + + describe "when a product goes out of stock just before it's added to the cart" do + before do + variant.update_attributes! on_hand: 0 + end + + it "stops the attempt, shows an error message and refreshes the products asynchronously" do + # -- Messaging + alert_message = accept_alert do + fill_in "variants[#{variant.id}]", with: '1' + wait_until { !cart_dirty } + end + + # TODO: This will be a modal + expect(alert_message).to be + puts alert_message + + # -- Page updates + # Update amount in cart + page.should have_field "variants[#{variant.id}]", with: '0', disabled: true + page.should have_field "variants[#{variant2.id}]", with: '' + + # Update amount available in product list + # If amount falls to zero, variant should be greyed out and input disabled + page.should have_selector "#variant-#{variant.id}.out-of-stock" + page.should have_selector "#variants_#{variant.id}[max='0']" + page.should have_selector "#variants_#{variant.id}[disabled='disabled']" + end + + it "does the same for group buy products" + end end context "when no order cycles are available" do From 5e39b11c2f2d81274f3f6fff956d3e84dfae0fc3 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 8 Apr 2016 11:44:28 +1000 Subject: [PATCH 018/101] Spec out of stock handling for group buy --- .../shop_variant_with_group_buy.html.haml | 3 +- .../consumer/shopping/shopping_spec.rb | 37 ++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml index 51f7599af7..c44a71948b 100644 --- a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml +++ b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml @@ -19,4 +19,5 @@ placeholder: "{{'shop_variant_quantity_max' | t}}", "ofn-disable-scroll" => true, max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", - name: "variant_attributes[{{variant.id}}][max_quantity]"} + name: "variant_attributes[{{variant.id}}][max_quantity]", + id: "variants_{{variant.id}}_max"} diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index ab94c678d7..c252a3f041 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -239,11 +239,9 @@ feature "As a consumer I want to shop with a distributor", js: true do end describe "when a product goes out of stock just before it's added to the cart" do - before do - variant.update_attributes! on_hand: 0 - end - it "stops the attempt, shows an error message and refreshes the products asynchronously" do + variant.update_attributes! on_hand: 0 + # -- Messaging alert_message = accept_alert do fill_in "variants[#{variant.id}]", with: '1' @@ -266,7 +264,36 @@ feature "As a consumer I want to shop with a distributor", js: true do page.should have_selector "#variants_#{variant.id}[disabled='disabled']" end - it "does the same for group buy products" + context "group buy products" do + let(:product) { create(:simple_product, group_buy: true) } + + it "does the same" do + # -- Place in cart so we can set max_quantity, then make out of stock + fill_in "variants[#{variant.id}]", with: '1' + wait_until { !cart_dirty } + variant.update_attributes! on_hand: 0 + + # -- Messaging + alert_message = accept_alert do + fill_in "variant_attributes[#{variant.id}][max_quantity]", with: '1' + wait_until { !cart_dirty } + end + + # TODO: This will be a modal + expect(alert_message).to be + puts alert_message + + # -- Page updates + # Update amount in cart + page.should have_field "variant_attributes[#{variant.id}][max_quantity]", with: '0', disabled: true + + # Update amount available in product list + # If amount falls to zero, variant should be greyed out and input disabled + page.should have_selector "#variant-#{variant.id}.out-of-stock" + page.should have_selector "#variants_#{variant.id}_max[max='0']" + page.should have_selector "#variants_#{variant.id}_max[disabled='disabled']" + end + end end end From 35117f7af433bc31f1a9be9cacf2f966b2d3b28b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 8 Apr 2016 15:39:25 +1000 Subject: [PATCH 019/101] Show a modal when available stock levels have reduced --- .../darkswarm/services/cart.js.coffee | 17 +++++++++--- .../templates/out_of_stock.html.haml | 10 +++++++ .../spree/orders_controller_decorator.rb | 8 ++++-- .../spree/orders_controller_spec.rb | 8 ++++++ .../consumer/shopping/shopping_spec.rb | 26 +++++++++---------- 5 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 app/assets/javascripts/templates/out_of_stock.html.haml diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index 973170e414..4f808e0710 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, storage)-> +Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $rootScope, storage)-> # Handles syncing of current cart/order state to server new class Cart dirty: false @@ -41,21 +41,30 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, storage)-> @update_running = false compareAndNotifyStockLevels: (stockLevels) => + scope = $rootScope.$new(true) + scope.variants = [] + + # TODO: These changes to quantity/max_quantity trigger another cart update, which + # is unnecessary. + for li in @line_items_present() if !stockLevels[li.variant.id]? - alert "Variant out of stock: #{li.variant.id}" li.quantity = 0 li.max_quantity = 0 if li.max_quantity? li.variant.count_on_hand = 0 + scope.variants.push li.variant else if stockLevels[li.variant.id].quantity < li.quantity - alert "Variant quantity reduced: #{li.variant.id}" li.quantity = stockLevels[li.variant.id].quantity + scope.variants.push li.variant if stockLevels[li.variant.id].max_quantity < li.max_quantity - alert "Variant max_quantity reduced: #{li.variant.id}" li.max_quantity = stockLevels[li.variant.id].max_quantity + scope.variants.push li.variant li.variant.count_on_hand = stockLevels[li.variant.id].on_hand + if scope.variants.length > 0 + $modal.open(templateUrl: "out_of_stock.html", scope: scope, windowClass: 'out-of-stock-modal') + popQueue: => @update_enqueued = false @scheduleUpdate() diff --git a/app/assets/javascripts/templates/out_of_stock.html.haml b/app/assets/javascripts/templates/out_of_stock.html.haml new file mode 100644 index 0000000000..72244d7ab6 --- /dev/null +++ b/app/assets/javascripts/templates/out_of_stock.html.haml @@ -0,0 +1,10 @@ +%h3 Reduced stock available + +%p While you've been shopping, the stock levels for one or more of the products in your cart have reduced. Here's what's changed: + +%p{'ng-repeat' => "v in variants"} + %em {{ v.name_to_display }} - {{ v.unit_to_display }} + %span{'ng-if' => "v.count_on_hand == 0"} + is now out of stock. + %span{'ng-if' => "v.count_on_hand > 0"} + now only has {{ v.count_on_hand }} remaining. diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index 4c2ab8cb63..836b77b271 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -51,12 +51,11 @@ Spree::OrdersController.class_eval do [li.variant.id, {quantity: li.quantity, max_quantity: li.max_quantity, - on_hand: li.variant.on_hand}] + on_hand: wrap_json_infinity(li.variant.on_hand)}] end ] end - def update_distribution @order = current_order(true) @@ -135,4 +134,9 @@ Spree::OrdersController.class_eval do end end + # Rails to_json encodes Float::INFINITY as Infinity, which is not valid JSON + # Return it as a large integer (max 32 bit signed int) + def wrap_json_infinity(n) + n == Float::INFINITY ? 2147483647 : n + end end diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index 756be1d3ec..df7be3508d 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -69,6 +69,14 @@ describe Spree::OrdersController do it "returns a hash with variant id, quantity, max_quantity and stock on hand" do controller.stock_levels.should == {v.id => {quantity: 2, max_quantity: 3, on_hand: 4}} end + + describe "encoding Infinity" do + let!(:v) { create(:variant, on_demand: true, count_on_hand: 0) } + + it "encodes Infinity as a large, finite integer" do + controller.stock_levels.should == {v.id => {quantity: 2, max_quantity: 3, on_hand: 2147483647}} + end + end end end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index c252a3f041..b0d5a42127 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -243,14 +243,13 @@ feature "As a consumer I want to shop with a distributor", js: true do variant.update_attributes! on_hand: 0 # -- Messaging - alert_message = accept_alert do - fill_in "variants[#{variant.id}]", with: '1' - wait_until { !cart_dirty } - end + fill_in "variants[#{variant.id}]", with: '1' + wait_until { !cart_dirty } - # TODO: This will be a modal - expect(alert_message).to be - puts alert_message + within(".out-of-stock-modal") do + page.should have_content "stock levels for one or more of the products in your cart have reduced" + page.should have_content "#{product.name} - #{variant.unit_to_display} is now out of stock." + end # -- Page updates # Update amount in cart @@ -274,14 +273,13 @@ feature "As a consumer I want to shop with a distributor", js: true do variant.update_attributes! on_hand: 0 # -- Messaging - alert_message = accept_alert do - fill_in "variant_attributes[#{variant.id}][max_quantity]", with: '1' - wait_until { !cart_dirty } - end + fill_in "variant_attributes[#{variant.id}][max_quantity]", with: '1' + wait_until { !cart_dirty } - # TODO: This will be a modal - expect(alert_message).to be - puts alert_message + within(".out-of-stock-modal") do + page.should have_content "stock levels for one or more of the products in your cart have reduced" + page.should have_content "#{product.name} - #{variant.unit_to_display} is now out of stock." + end # -- Page updates # Update amount in cart From dac90c8003d5599d592f17f9dbc5a80f146827ed Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 11 Apr 2016 08:42:03 +1000 Subject: [PATCH 020/101] Fix specs --- spec/controllers/checkout_controller_spec.rb | 4 ++-- spec/features/consumer/shopping/checkout_auth_spec.rb | 11 +++++------ spec/features/consumer/shopping/shopping_spec.rb | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/spec/controllers/checkout_controller_spec.rb b/spec/controllers/checkout_controller_spec.rb index ddacd0fdeb..1f5e40a841 100644 --- a/spec/controllers/checkout_controller_spec.rb +++ b/spec/controllers/checkout_controller_spec.rb @@ -34,13 +34,13 @@ describe CheckoutController do flash[:info].should == "The hub you have selected is temporarily closed for orders. Please try again later." end - it "redirects to the shop when no line items are present" do + it "redirects to the cart when some items are out of stock" do controller.stub(:current_distributor).and_return(distributor) controller.stub(:current_order_cycle).and_return(order_cycle) controller.stub(:current_order).and_return(order) order.stub_chain(:insufficient_stock_lines, :present?).and_return true get :edit - response.should redirect_to shop_path + response.should redirect_to spree.cart_path end it "renders when both distributor and order cycle is selected" do diff --git a/spec/features/consumer/shopping/checkout_auth_spec.rb b/spec/features/consumer/shopping/checkout_auth_spec.rb index a19776f7a9..0b0683def0 100644 --- a/spec/features/consumer/shopping/checkout_auth_spec.rb +++ b/spec/features/consumer/shopping/checkout_auth_spec.rb @@ -9,7 +9,7 @@ feature "As a consumer I want to check out my cart", js: true do let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) } let(:supplier) { create(:supplier_enterprise) } - let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.master]) } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } let(:product) { create(:simple_product, supplier: supplier) } let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } let(:address) { create(:address, firstname: "Foo", lastname: "Bar") } @@ -23,7 +23,7 @@ feature "As a consumer I want to check out my cart", js: true do it "does not not render the login form when logged in" do quick_login_as user - visit checkout_path + visit checkout_path within "section[role='main']" do page.should_not have_content "Login" page.should have_checkout_details @@ -31,7 +31,7 @@ feature "As a consumer I want to check out my cart", js: true do end it "renders the login buttons when logged out" do - visit checkout_path + visit checkout_path within "section[role='main']" do page.should have_content "Login" click_button "Login" @@ -53,9 +53,8 @@ feature "As a consumer I want to check out my cart", js: true do end it "allows user to checkout as guest" do - visit checkout_path + visit checkout_path checkout_as_guest - page.should have_checkout_details + page.should have_checkout_details end end - diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index b0d5a42127..41a68dfb9b 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -318,7 +318,7 @@ feature "As a consumer I want to shop with a distributor", js: true do let(:variant) { create(:variant, product: product) } before do - add_product_and_variant_to_order_cycle(exchange, product, variant) + add_variant_to_order_cycle(exchange, variant) set_order_cycle(order, oc1) distributor.require_login = true distributor.save! From b2d78e7df6f1cb50913ece0fab5a7db5cb48d913 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 11 Apr 2016 08:55:05 +1000 Subject: [PATCH 021/101] Set allow_backorders explicitly for consistency in CI --- spec/models/spree/order_populator_spec.rb | 33 +++++++++++++---------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/spec/models/spree/order_populator_spec.rb b/spec/models/spree/order_populator_spec.rb index 9f7cc47c51..4a50e59fa8 100644 --- a/spec/models/spree/order_populator_spec.rb +++ b/spec/models/spree/order_populator_spec.rb @@ -194,23 +194,28 @@ module Spree describe "quantities_to_add" do let(:v) { double(:variant, on_hand: 10) } - context "when max_quantity is not provided" do - it "returns full amount when available" do - op.quantities_to_add(v, 5, nil).should == [5, nil] + + context "when backorders are not allowed" do + before { Spree::Config.allow_backorders = false } + + context "when max_quantity is not provided" do + it "returns full amount when available" do + op.quantities_to_add(v, 5, nil).should == [5, nil] + end + + it "returns a limited amount when not entirely available" do + op.quantities_to_add(v, 15, nil).should == [10, nil] + end end - it "returns a limited amount when not entirely available" do - op.quantities_to_add(v, 15, nil).should == [10, nil] - end - end + context "when max_quantity is provided" do + it "returns full amount when available" do + op.quantities_to_add(v, 5, 6).should == [5, 6] + end - context "when max_quantity is provided" do - it "returns full amount when available" do - op.quantities_to_add(v, 5, 6).should == [5, 6] - end - - it "returns a limited amount when not entirely available" do - op.quantities_to_add(v, 15, 16).should == [10, 10] + it "returns a limited amount when not entirely available" do + op.quantities_to_add(v, 15, 16).should == [10, 10] + end end end From 9b3139dba9d2d71a7b09ec99afd24590933c31aa Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 12 Apr 2016 11:18:03 +1000 Subject: [PATCH 022/101] When there's an out of stock product in the cart, visiting the shopfront returns user to the cart --- app/controllers/enterprises_controller.rb | 12 +++++++++- .../enterprises_controller_spec.rb | 24 ++++++++++++++++++- spec/controllers/shop_controller_spec.rb | 11 --------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index 8a875c76bf..9459cf43e3 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -5,6 +5,8 @@ class EnterprisesController < BaseController # These prepended filters are in the reverse order of execution prepend_before_filter :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop + before_filter :check_stock_levels, only: :shop + before_filter :clean_permalink, only: :check_permalink respond_to :js, only: :permalink_checker @@ -21,17 +23,25 @@ class EnterprisesController < BaseController end end + private def clean_permalink params[:permalink] = params[:permalink].parameterize end + def check_stock_levels + if current_order(true).insufficient_stock_lines.present? + flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) + redirect_to spree.cart_path + end + end + def reset_order distributor = Enterprise.is_distributor.find_by_permalink(params[:id]) || Enterprise.is_distributor.find(params[:id]) order = current_order(true) - if order.distributor and order.distributor != distributor + if order.distributor && order.distributor != distributor order.empty! order.set_order_cycle! nil end diff --git a/spec/controllers/enterprises_controller_spec.rb b/spec/controllers/enterprises_controller_spec.rb index b3cb6b5e32..0727488841 100644 --- a/spec/controllers/enterprises_controller_spec.rb +++ b/spec/controllers/enterprises_controller_spec.rb @@ -2,13 +2,14 @@ require 'spec_helper' describe EnterprisesController do describe "shopping for a distributor" do + let(:order) { controller.current_order(true) } before(:each) do @current_distributor = create(:distributor_enterprise, with_payment_and_shipping: true) @distributor = create(:distributor_enterprise, with_payment_and_shipping: true) @order_cycle1 = create(:simple_order_cycle, distributors: [@distributor], orders_open_at: 2.days.ago, orders_close_at: 3.days.from_now ) @order_cycle2 = create(:simple_order_cycle, distributors: [@distributor], orders_open_at: 3.days.ago, orders_close_at: 4.days.from_now ) - controller.current_order(true).distributor = @current_distributor + order.set_distributor! @current_distributor end it "sets the shop as the distributor on the order when shopping for the distributor" do @@ -52,6 +53,27 @@ describe EnterprisesController do controller.current_order.line_items.size.should == 1 end + describe "when an out of stock item is in the cart" do + let(:variant) { create(:variant, on_demand: false, on_hand: 10) } + let(:line_item) { create(:line_item, variant: variant) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [@distributor], variants: [variant]) } + + before do + order.set_distribution! @current_distributor, order_cycle + order.line_items << line_item + + Spree::Config.set allow_backorders: false + variant.on_hand = 0 + variant.save! + end + + it "redirects to the cart" do + spree_get :shop, {id: @current_distributor} + + response.should redirect_to spree.cart_path + end + end + it "sets order cycle if only one is available at the chosen distributor" do @order_cycle2.destroy diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index 73231bc86c..a4ddab3ad3 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -69,17 +69,6 @@ describe ShopController do end - describe "producers/suppliers" do - let(:supplier) { create(:supplier_enterprise) } - let(:product) { create(:product, supplier: supplier) } - let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) } - - before do - 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: [distributor]) } let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first } From 3dcfa810fdec28cc5f9e81886327ba3e68d3f5f1 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 14 Apr 2016 11:14:16 +1000 Subject: [PATCH 023/101] Display out of stock banner when viewing cart directly --- app/controllers/checkout_controller.rb | 2 -- app/controllers/enterprises_controller.rb | 1 - .../spree/orders_controller_decorator.rb | 12 +++++++--- .../spree/orders_controller_spec.rb | 22 ++++++++++++++++++- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 7b34bbcff5..c6d1291477 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -153,12 +153,10 @@ class CheckoutController < Spree::CheckoutController def raise_insufficient_quantity respond_to do |format| format.html do - flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) redirect_to cart_path end format.json do - flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) render json: {path: cart_path}, status: 400 end end diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index 9459cf43e3..fe2eda8bfd 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -32,7 +32,6 @@ class EnterprisesController < BaseController def check_stock_levels if current_order(true).insufficient_stock_lines.present? - flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) redirect_to spree.cart_path end end diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index 836b77b271..1b542f0829 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -1,9 +1,9 @@ require 'spree/core/controller_helpers/order_decorator' Spree::OrdersController.class_eval do - after_filter :populate_variant_attributes, :only => :populate - before_filter :update_distribution, :only => :update - before_filter :filter_order_params, :only => :update + after_filter :populate_variant_attributes, only: :populate + before_filter :update_distribution, only: :update + before_filter :filter_order_params, only: :update prepend_before_filter :require_order_cycle, only: :edit prepend_before_filter :require_distributor_chosen, only: :edit @@ -12,13 +12,19 @@ Spree::OrdersController.class_eval do include OrderCyclesHelper layout 'darkswarm' + # Patching to redirect to shop if order is empty def edit @order = current_order(true) + if @order.line_items.empty? redirect_to main_app.shop_path else associate_user + + if @order.insufficient_stock_lines.present? + flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) + end end end diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index df7be3508d..6baa633378 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -42,6 +42,26 @@ describe Spree::OrdersController do flash[:info].should == "The hub you have selected is temporarily closed for orders. Please try again later." end + describe "when an item has insufficient stock" do + let(:order) { subject.current_order(true) } + let(:oc) { create(:simple_order_cycle, distributors: [d], variants: [variant]) } + let(:d) { create(:distributor_enterprise, shipping_methods: [create(:shipping_method)], payment_methods: [create(:payment_method)]) } + let(:variant) { create(:variant, on_demand: false, on_hand: 5) } + let(:line_item) { order.line_items.last } + + before do + order.set_distribution! d, oc + order.add_variant variant, 5 + variant.update_attributes! on_hand: 3 + end + + it "displays a flash message when we view the cart" do + spree_get :edit + expect(response.status).to eq 200 + flash[:error].should == "An item in your cart has become unavailable." + end + end + describe "returning stock levels in JSON on success" do let(:product) { create(:simple_product) } @@ -120,7 +140,7 @@ describe Spree::OrdersController do describe "when I pass params that includes a line item no longer in our cart" do it "should silently ignore the missing line item" do order = subject.current_order(true) - li = order.add_variant(create(:simple_product, on_hand: 110).master) + li = order.add_variant(create(:simple_product, on_hand: 110).variants.first) spree_get :update, order: { line_items_attributes: { "0" => {quantity: "0", id: "9999"}, "1" => {quantity: "99", id: li.id} From 5151779f805b33b6d094b16297788689e88ddce0 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 14 Apr 2016 15:20:44 +1000 Subject: [PATCH 024/101] When update is for another line item, still update all stock levels and show warnings --- .../spree/orders_controller_decorator.rb | 1 + app/models/spree/line_item_decorator.rb | 10 ++++++ app/models/spree/order_decorator.rb | 5 +++ .../consumer/shopping/shopping_spec.rb | 36 +++++++++++++++++++ spec/models/spree/line_item_spec.rb | 35 ++++++++++++++++++ 5 files changed, 87 insertions(+) diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index 1b542f0829..f9c7f53001 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -41,6 +41,7 @@ Spree::OrdersController.class_eval do fire_event('spree.cart.add') fire_event('spree.order.contents_changed') + current_order.cap_quantity_at_stock! current_order.update! render json: {error: false, stock_levels: stock_levels}, status: 200 diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index 9db9ae1269..b9f8af577f 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -43,6 +43,16 @@ Spree::LineItem.class_eval do where('spree_adjustments.id IS NULL') + def cap_quantity_at_stock! + attrs = {} + + attrs[:quantity] = variant.on_hand if quantity > variant.on_hand + attrs[:max_quantity] = variant.on_hand if (max_quantity || 0) > variant.on_hand + + update_attributes!(attrs) if attrs.any? + end + + def has_tax? adjustments.included_tax.any? end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 53a1b81873..a06a34bcd0 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -144,6 +144,11 @@ Spree::Order.class_eval do current_item end + def cap_quantity_at_stock! + line_items.each &:cap_quantity_at_stock! + end + + def set_distributor!(distributor) self.distributor = distributor self.order_cycle = nil unless self.order_cycle.andand.has_distributor? distributor diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 41a68dfb9b..9dc0a39dac 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -292,6 +292,42 @@ feature "As a consumer I want to shop with a distributor", js: true do page.should have_selector "#variants_#{variant.id}_max[disabled='disabled']" end end + + context "when the update is for another product" do + it "updates quantity" do + fill_in "variants[#{variant.id}]", with: '1' + wait_until { !cart_dirty } + + variant.update_attributes! on_hand: 0 + + fill_in "variants[#{variant2.id}]", with: '1' + wait_until { !cart_dirty } + + within(".out-of-stock-modal") do + page.should have_content "stock levels for one or more of the products in your cart have reduced" + page.should have_content "#{product.name} - #{variant.unit_to_display} is now out of stock." + end + end + + context "group buy products" do + let(:product) { create(:simple_product, group_buy: true) } + + it "updates max_quantity" do + fill_in "variants[#{variant.id}]", with: '1' + fill_in "variant_attributes[#{variant.id}][max_quantity]", with: '2' + wait_until { !cart_dirty } + variant.update_attributes! on_hand: 1 + + fill_in "variants[#{variant2.id}]", with: '1' + wait_until { !cart_dirty } + + within(".out-of-stock-modal") do + page.should have_content "stock levels for one or more of the products in your cart have reduced" + page.should have_content "#{product.name} - #{variant.unit_to_display} now only has 1 remaining" + end + end + end + end end end diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index 6bb18fafa3..2b5362e94f 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -42,6 +42,41 @@ module Spree end end + describe "capping quantity at stock level" do + let!(:v) { create(:variant, on_demand: false, on_hand: 10) } + let!(:li) { create(:line_item, variant: v, quantity: 10, max_quantity: 10) } + + before do + v.update_attributes! on_hand: 5 + end + + it "caps quantity" do + li.cap_quantity_at_stock! + li.reload.quantity.should == 5 + end + + it "caps max_quantity" do + li.cap_quantity_at_stock! + li.reload.max_quantity.should == 5 + end + + it "works for products without max_quantity" do + li.update_column :max_quantity, nil + li.cap_quantity_at_stock! + li.reload + li.quantity.should == 5 + li.max_quantity.should be_nil + end + + it "does nothing for on_demand items" do + v.update_attributes! on_demand: true + li.cap_quantity_at_stock! + li.reload + li.quantity.should == 10 + li.max_quantity.should == 10 + end + end + describe "calculating price with adjustments" do it "does not return fractional cents" do li = LineItem.new From 06d7665bf97fda4f4ad9a71b1c0bfba5360f2949 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 Apr 2016 09:54:56 +1000 Subject: [PATCH 025/101] Prospective fix for intermittent spec fail --- spec/controllers/spree/orders_controller_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index 6baa633378..c0a844a4bc 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -50,6 +50,7 @@ describe Spree::OrdersController do let(:line_item) { order.line_items.last } before do + Spree::Config.allow_backorders = false order.set_distribution! d, oc order.add_variant variant, 5 variant.update_attributes! on_hand: 3 From 6cba935a6561e0afdc7b66099939c8f020bca13f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 Apr 2016 11:26:30 +1000 Subject: [PATCH 026/101] Add close button to out of stock modal --- app/assets/javascripts/templates/out_of_stock.html.haml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/javascripts/templates/out_of_stock.html.haml b/app/assets/javascripts/templates/out_of_stock.html.haml index 72244d7ab6..d894583fcf 100644 --- a/app/assets/javascripts/templates/out_of_stock.html.haml +++ b/app/assets/javascripts/templates/out_of_stock.html.haml @@ -1,3 +1,6 @@ +%a.close-reveal-modal{"ng-click" => "$close()"} + %i.ofn-i_009-close + %h3 Reduced stock available %p While you've been shopping, the stock levels for one or more of the products in your cart have reduced. Here's what's changed: From 779be7c5a0657d1fccba5cf35dfe197a4f3843e1 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 21 Apr 2016 16:37:15 +1000 Subject: [PATCH 027/101] Extract params parsing into single method --- app/models/spree/order_populator_decorator.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/spree/order_populator_decorator.rb b/app/models/spree/order_populator_decorator.rb index 82d9aa85e7..38c908a530 100644 --- a/app/models/spree/order_populator_decorator.rb +++ b/app/models/spree/order_populator_decorator.rb @@ -11,8 +11,7 @@ Spree::OrderPopulator.class_eval do if valid? @order.with_lock do - variants = read_products_hash(from_hash) + - read_variants_hash(from_hash) + variants = read_variants from_hash variants.each do |v| if varies_from_cart(v) @@ -31,6 +30,11 @@ Spree::OrderPopulator.class_eval do valid? end + def read_variants(data) + read_products_hash(data) + + read_variants_hash(data) + end + def read_products_hash(data) (data[:products] || []).map do |product_id, variant_id| {variant_id: variant_id, quantity: data[:quantity]} From 28d40bf27d3418d3e8b89bec03e65a83887be3a7 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 21 Apr 2016 21:27:52 +1000 Subject: [PATCH 028/101] Fixing font styling on enterprise name in shop product summary --- app/views/shop/products/_summary.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml index 6d36ac81ce..8671b00cbf 100644 --- a/app/views/shop/products/_summary.html.haml +++ b/app/views/shop/products/_summary.html.haml @@ -13,8 +13,9 @@ %em = t :products_from %span - %enterprise-modal - %i.ofn-i_036-producers{"bo-text" => "enterprise.name"} + %enterprise-modal + %i.ofn-i_036-producers + %span{"bo-bind" => "enterprise.name"} .small-2.medium-2.large-1.columns.text-center .taxon-flag %render-svg{path: "{{product.primary_taxon.icon}}"} From a26266159cefcdc55c2e1bff7d5498a9d88c069d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 22 Apr 2016 10:47:20 +1000 Subject: [PATCH 029/101] Fix timing issue: change in client-side value during server update --- .../darkswarm/services/cart.js.coffee | 20 ++++----- .../spree/orders_controller_decorator.rb | 26 ++++++++++-- app/models/spree/order_populator_decorator.rb | 6 ++- .../spree/orders_controller_spec.rb | 30 ++++++++++++-- .../darkswarm/services/cart_spec.js.coffee | 41 +++++++++++++++++-- 5 files changed, 100 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index 4f808e0710..d75ef7a628 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -28,6 +28,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo update: => @update_running = true + $http.post('/orders/populate', @data()).success (data, status)=> @saved() @update_running = false @@ -48,19 +49,14 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo # is unnecessary. for li in @line_items_present() - if !stockLevels[li.variant.id]? - li.quantity = 0 - li.max_quantity = 0 if li.max_quantity? - li.variant.count_on_hand = 0 - scope.variants.push li.variant - else - if stockLevels[li.variant.id].quantity < li.quantity - li.quantity = stockLevels[li.variant.id].quantity - scope.variants.push li.variant - if stockLevels[li.variant.id].max_quantity < li.max_quantity - li.max_quantity = stockLevels[li.variant.id].max_quantity - scope.variants.push li.variant + if stockLevels[li.variant.id]? li.variant.count_on_hand = stockLevels[li.variant.id].on_hand + if li.quantity > li.variant.count_on_hand + li.quantity = li.variant.count_on_hand + scope.variants.push li.variant + if li.max_quantity > li.variant.count_on_hand + li.max_quantity = li.variant.count_on_hand + scope.variants.push(li.variant) unless li.variant in scope.variants if scope.variants.length > 0 $modal.open(templateUrl: "out_of_stock.html", scope: scope, windowClass: 'out-of-stock-modal') diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index f9c7f53001..e675ce7e94 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -44,7 +44,10 @@ Spree::OrdersController.class_eval do current_order.cap_quantity_at_stock! current_order.update! - render json: {error: false, stock_levels: stock_levels}, status: 200 + variant_ids = variant_ids_in(populator.variants_h) + + render json: {error: false, stock_levels: stock_levels(current_order, variant_ids)}, + status: 200 else render json: {error: true}, status: 412 @@ -52,9 +55,26 @@ Spree::OrdersController.class_eval do end end - def stock_levels + # Report the stock levels in the order for all variant ids requested + def stock_levels(order, variant_ids) + stock_levels = li_stock_levels(order) + + li_variant_ids = stock_levels.keys + (variant_ids - li_variant_ids).each do |variant_id| + stock_levels[variant_id] = {quantity: 0, max_quantity: 0, + on_hand: Spree::Variant.find(variant_id).on_hand} + end + + stock_levels + end + + def variant_ids_in(variants_h) + variants_h.map { |v| v[:variant_id].to_i } + end + + def li_stock_levels(order) Hash[ - current_order.line_items.map do |li| + order.line_items.map do |li| [li.variant.id, {quantity: li.quantity, max_quantity: li.max_quantity, diff --git a/app/models/spree/order_populator_decorator.rb b/app/models/spree/order_populator_decorator.rb index 38c908a530..a106de6936 100644 --- a/app/models/spree/order_populator_decorator.rb +++ b/app/models/spree/order_populator_decorator.rb @@ -1,6 +1,8 @@ require 'open_food_network/scope_variant_to_hub' Spree::OrderPopulator.class_eval do + attr_reader :variants_h + def populate(from_hash, overwrite = false) @distributor, @order_cycle = distributor_and_order_cycle # Refactor: We may not need this validation - we can't change distribution here, so @@ -31,8 +33,8 @@ Spree::OrderPopulator.class_eval do end def read_variants(data) - read_products_hash(data) + - read_variants_hash(data) + @variants_h = read_products_hash(data) + + read_variants_hash(data) end def read_products_hash(data) diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index c0a844a4bc..286bb9217a 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -67,9 +67,11 @@ describe Spree::OrdersController do let(:product) { create(:simple_product) } it "returns stock levels as JSON" do + controller.stub(:variant_ids_in) { [123] } controller.stub(:stock_levels) { 'my_stock_levels' } Spree::OrderPopulator.stub(:new).and_return(populator = double()) populator.stub(:populate) { true } + populator.stub(:variants_h) { {} } xhr :post, :populate, use_route: :spree, format: :json @@ -81,6 +83,7 @@ describe Spree::OrdersController do let!(:order) { create(:order) } let!(:li) { create(:line_item, order: order, variant: v, quantity: 2, max_quantity: 3) } let!(:v) { create(:variant, count_on_hand: 4) } + let!(:v2) { create(:variant, count_on_hand: 2) } before do order.reload @@ -88,17 +91,37 @@ describe Spree::OrdersController do end it "returns a hash with variant id, quantity, max_quantity and stock on hand" do - controller.stock_levels.should == {v.id => {quantity: 2, max_quantity: 3, on_hand: 4}} + controller.stock_levels(order, [v.id]).should == + {v.id => {quantity: 2, max_quantity: 3, on_hand: 4}} + end + + it "includes all line items, even when the variant_id is not specified" do + controller.stock_levels(order, []).should == + {v.id => {quantity: 2, max_quantity: 3, on_hand: 4}} + end + + it "includes an empty quantity entry for variants that aren't in the order" do + controller.stock_levels(order, [v.id, v2.id]).should == + {v.id => {quantity: 2, max_quantity: 3, on_hand: 4}, + v2.id => {quantity: 0, max_quantity: 0, on_hand: 2}} end describe "encoding Infinity" do let!(:v) { create(:variant, on_demand: true, count_on_hand: 0) } it "encodes Infinity as a large, finite integer" do - controller.stock_levels.should == {v.id => {quantity: 2, max_quantity: 3, on_hand: 2147483647}} + controller.stock_levels(order, [v.id]).should == + {v.id => {quantity: 2, max_quantity: 3, on_hand: 2147483647}} end end end + + it "extracts variant ids from the populator" do + variants_h = [{:variant_id=>"900", :quantity=>2, :max_quantity=>nil}, + {:variant_id=>"940", :quantity=>3, :max_quantity=>3}] + + controller.variant_ids_in(variants_h).should == [900, 940] + end end context "adding a group buy product to the cart" do @@ -118,7 +141,8 @@ describe Spree::OrdersController do it "returns HTTP success when successful" do Spree::OrderPopulator.stub(:new).and_return(populator = double()) - populator.stub(:populate).and_return true + populator.stub(:populate) { true } + populator.stub(:variants_h) { {} } xhr :post, :populate, use_route: :spree, format: :json response.status.should == 200 end diff --git a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee index 58b7795124..6df74e3ede 100644 --- a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee @@ -130,21 +130,24 @@ describe 'Cart service', -> describe "when an item is out of stock", -> it "reduces the quantity in the cart", -> li = {variant: {id: 1}, quantity: 5} + stockLevels = {1: {quantity: 0, max_quantity: 0, on_hand: 0}} spyOn(Cart, 'line_items_present').andReturn [li] - Cart.compareAndNotifyStockLevels {} + Cart.compareAndNotifyStockLevels stockLevels expect(li.quantity).toEqual 0 expect(li.max_quantity).toBeUndefined() it "reduces the max_quantity in the cart", -> li = {variant: {id: 1}, quantity: 5, max_quantity: 6} + stockLevels = {1: {quantity: 0, max_quantity: 0, on_hand: 0}} spyOn(Cart, 'line_items_present').andReturn [li] - Cart.compareAndNotifyStockLevels {} + Cart.compareAndNotifyStockLevels stockLevels expect(li.max_quantity).toEqual 0 it "resets the count on hand available", -> li = {variant: {id: 1, count_on_hand: 10}, quantity: 5} + stockLevels = {1: {quantity: 0, max_quantity: 0, on_hand: 0}} spyOn(Cart, 'line_items_present').andReturn [li] - Cart.compareAndNotifyStockLevels {} + Cart.compareAndNotifyStockLevels stockLevels expect(li.variant.count_on_hand).toEqual 0 describe "when the quantity available is less than that requested", -> @@ -170,6 +173,38 @@ describe 'Cart service', -> Cart.compareAndNotifyStockLevels stockLevels expect(li.variant.count_on_hand).toEqual 6 + describe "when the client-side quantity has been increased during the request", -> + it "does not reset the quantity", -> + li = {variant: {id: 1}, quantity: 6} + stockLevels = {1: {quantity: 5, on_hand: 6}} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels stockLevels + expect(li.quantity).toEqual 6 + expect(li.max_quantity).toBeUndefined() + + it "does not reset the max_quantity", -> + li = {variant: {id: 1}, quantity: 5, max_quantity: 7} + stockLevels = {1: {quantity: 5, max_quantity: 6, on_hand: 7}} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels stockLevels + expect(li.quantity).toEqual 5 + expect(li.max_quantity).toEqual 7 + + describe "when the client-side quantity has been changed from 0 to 1 during the request", -> + it "does not reset the quantity", -> + li = {variant: {id: 1}, quantity: 1} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels {} + expect(li.quantity).toEqual 1 + expect(li.max_quantity).toBeUndefined() + + it "does not reset the max_quantity", -> + li = {variant: {id: 1}, quantity: 1, max_quantity: 1} + spyOn(Cart, 'line_items_present').andReturn [li] + Cart.compareAndNotifyStockLevels {} + expect(li.quantity).toEqual 1 + expect(li.max_quantity).toEqual 1 + it "pops the queue", -> Cart.update_enqueued = true spyOn(Cart, 'scheduleUpdate') From 23e598f2f8639e8635892cee1bbd360d43bdc675 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 Apr 2016 14:45:22 +1000 Subject: [PATCH 030/101] Destroy customer without flash notice --- app/controllers/admin/customers_controller.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/controllers/admin/customers_controller.rb b/app/controllers/admin/customers_controller.rb index bd865c8130..24c26661e5 100644 --- a/app/controllers/admin/customers_controller.rb +++ b/app/controllers/admin/customers_controller.rb @@ -26,6 +26,23 @@ module Admin end end + # copy of Spree::Admin::ResourceController without flash notice + def destroy + invoke_callbacks(:destroy, :before) + if @object.destroy + invoke_callbacks(:destroy, :after) + respond_with(@object) do |format| + format.html { redirect_to location_after_destroy } + format.js { render partial: "spree/admin/shared/destroy" } + end + else + invoke_callbacks(:destroy, :fails) + respond_with(@object) do |format| + format.html { redirect_to location_after_destroy } + end + end + end + private def collection From 2367b73d3a037a9d7e7a8b981aa29c382a2353b0 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 22 Apr 2016 14:53:08 +1000 Subject: [PATCH 031/101] Revert "Associate new users with existing customer records" This reverts commit a25f4fdf44cb218343a2df3ae94748be892b24bc. Since email addresses are not validated, these associations would allow an attacker to signup with the email address of another person and view their orders. --- app/models/spree/user_decorator.rb | 5 ----- spec/models/spree/user_spec.rb | 17 ----------------- 2 files changed, 22 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index be60307d52..17473fd521 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -15,7 +15,6 @@ 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 @@ -42,10 +41,6 @@ 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 923bc93183..c7d284b143 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -53,23 +53,6 @@ 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 From 7f38f1dd1c63f7b4b804fa05a519fcfb3bfa61e9 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 26 Apr 2016 03:14:13 +0100 Subject: [PATCH 032/101] Exclude Accounts & Billing distributor --- app/models/spree/user_decorator.rb | 5 ++++- spec/features/consumer/account_spec.rb | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 9a992ba910..7e3a38afb2 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -56,7 +56,10 @@ Spree.user_class.class_eval do # Returns Enterprise IDs for distributors that the user has shopped at def enterprises_ordered_from - orders.where(state: :complete).map(&:distributor_id).uniq + orders.where(state: :complete) + .where('distributor_id != ?', Spree::Config.accounts_distributor_id) + .map(&:distributor_id) + .uniq end # Returns orders and their associated payments for all distributors that have been ordered from diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb index b072a24357..1203d459de 100644 --- a/spec/features/consumer/account_spec.rb +++ b/spec/features/consumer/account_spec.rb @@ -13,6 +13,8 @@ feature %q{ let!(:distributor2) { create(:distributor_enterprise) } let!(:distributor_credit) { create(:distributor_enterprise) } let!(:distributor_without_orders) { create(:distributor_enterprise) } + let!(:accounts_distributor) {create :distributor_enterprise} + let!(:order_account_invoice) { create(:order, distributor: accounts_distributor, user: user) } let!(:d1o1) { create(:completed_order_with_totals, distributor_id: distributor1.id, user_id: user.id, total: 10000)} 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)} @@ -21,19 +23,24 @@ feature %q{ before do + Spree::Config.accounts_distributor_id = accounts_distributor.id credit_order.update! login_as user visit "/account" end it "shows all hubs that have been ordered from with balance or credit" do + # Single test to avoid re-rendering page expect(page).to have_content distributor1.name expect(page).to have_content distributor2.name expect(page).not_to have_content distributor_without_orders.name + # Exclude the special Accounts & Billing distributor + expect(page).not_to have_content accounts_distributor.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 expand_active_table_node distributor1.name expect(page).to have_link "Order " + d1o1.number, href:"/orders/#{d1o1.number}" From 43d6e49c3a372dba3021af7c834d4a7740b8af84 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 26 Apr 2016 03:31:56 +0100 Subject: [PATCH 033/101] Fix spec --- spec/features/consumer/account_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb index 1203d459de..58d4e1acc6 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!(:accounts_distributor) {create :distributor_enterprise} - let!(:order_account_invoice) { create(:order, distributor: accounts_distributor, user: user) } + let!(:order_account_invoice) { create(:order, distributor: accounts_distributor, state: 'complete', user: user) } let!(:d1o1) { create(:completed_order_with_totals, distributor_id: distributor1.id, user_id: user.id, total: 10000)} 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)} From ad6037ac63193a921ed9bb18c118b79acbd19290 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 26 Apr 2016 04:21:50 +0100 Subject: [PATCH 034/101] Fix user spec, check config is set --- app/models/spree/user_decorator.rb | 11 +++++++---- spec/models/spree/user_spec.rb | 8 +++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index ac62fee8d1..011bffc5dd 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -51,10 +51,13 @@ Spree.user_class.class_eval do # Returns Enterprise IDs for distributors that the user has shopped at def enterprises_ordered_from - orders.where(state: :complete) - .where('distributor_id != ?', Spree::Config.accounts_distributor_id) - .map(&:distributor_id) - .uniq + enterprise_ids = orders.where(state: :complete).map(&:distributor_id).uniq + # Exclude the accounts distributor + if Spree::Config.accounts_distributor_id + enterprise_ids = enterprise_ids.keep_if{|a| a != Spree::Config.accounts_distributor_id} + end + enterprise_ids + end # Returns orders and their associated payments for all distributors that have been ordered from diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index 0c15da5378..441757bf38 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -90,11 +90,17 @@ describe Spree.user_class do 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!(:accounts_distributor) {create :distributor_enterprise} + let!(:order_account_invoice) { create(:order, distributor: accounts_distributor, state: 'complete', user: u1) } let!(:completed_payment) { create(:payment, order: d1o1, state: 'completed') } let!(:payment) { create(:payment, order: d1o2, state: 'checkout') } - it "returns enterprises that the user has ordered from" do + before do + Spree::Config.accounts_distributor_id = accounts_distributor.id + end + + it "returns enterprises that the user has ordered from, excluding accounts distributor" do expect(u1.enterprises_ordered_from).to eq [distributor1.id] end From 2b921542a527bc0bb21f59c9281dbe5a366c1e5d Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 26 Apr 2016 04:24:44 +0100 Subject: [PATCH 035/101] Code styling --- app/models/spree/user_decorator.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 011bffc5dd..afd32bd294 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -54,10 +54,9 @@ Spree.user_class.class_eval do enterprise_ids = orders.where(state: :complete).map(&:distributor_id).uniq # Exclude the accounts distributor if Spree::Config.accounts_distributor_id - enterprise_ids = enterprise_ids.keep_if{|a| a != Spree::Config.accounts_distributor_id} + enterprise_ids = enterprise_ids.keep_if { |a| a != Spree::Config.accounts_distributor_id } end enterprise_ids - end # Returns orders and their associated payments for all distributors that have been ordered from From 8f8a1191cb2866d209985ddc7922c039773468a9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 22 Apr 2016 11:44:04 +1000 Subject: [PATCH 036/101] Remove stock cap on max_quantity --- .../darkswarm/services/cart.js.coffee | 2 +- .../shop_variant_with_group_buy.html.haml | 2 +- app/models/spree/line_item_decorator.rb | 7 +------ app/models/spree/order_populator_decorator.rb | 2 +- spec/features/consumer/shopping/shopping_spec.rb | 16 +++++++++------- .../unit/darkswarm/services/cart_spec.js.coffee | 4 ++-- spec/models/spree/line_item_spec.rb | 4 ++-- spec/models/spree/order_populator_spec.rb | 4 ++-- 8 files changed, 19 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index d75ef7a628..9ee03e26b3 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -54,7 +54,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo if li.quantity > li.variant.count_on_hand li.quantity = li.variant.count_on_hand scope.variants.push li.variant - if li.max_quantity > li.variant.count_on_hand + if li.variant.count_on_hand == 0 && li.max_quantity > li.variant.count_on_hand li.max_quantity = li.variant.count_on_hand scope.variants.push(li.variant) unless li.variant in scope.variants diff --git a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml index c44a71948b..f2b0b95837 100644 --- a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml +++ b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml @@ -18,6 +18,6 @@ "ng-model" => "variant.line_item.max_quantity", placeholder: "{{'shop_variant_quantity_max' | t}}", "ofn-disable-scroll" => true, - max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", + min: "{{variant.line_item.quantity}}", name: "variant_attributes[{{variant.id}}][max_quantity]", id: "variants_{{variant.id}}_max"} diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index b9f8af577f..8a89d8fdef 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -44,12 +44,7 @@ Spree::LineItem.class_eval do def cap_quantity_at_stock! - attrs = {} - - attrs[:quantity] = variant.on_hand if quantity > variant.on_hand - attrs[:max_quantity] = variant.on_hand if (max_quantity || 0) > variant.on_hand - - update_attributes!(attrs) if attrs.any? + update_attributes!(quantity: variant.on_hand) if quantity > variant.on_hand end diff --git a/app/models/spree/order_populator_decorator.rb b/app/models/spree/order_populator_decorator.rb index a106de6936..db03f1f2e2 100644 --- a/app/models/spree/order_populator_decorator.rb +++ b/app/models/spree/order_populator_decorator.rb @@ -77,7 +77,7 @@ Spree::OrderPopulator.class_eval do on_hand = variant.on_hand on_hand = [quantity, max_quantity].compact.max if Spree::Config.allow_backorders quantity_to_add = [quantity, on_hand].min - max_quantity_to_add = [max_quantity, on_hand].min if max_quantity + max_quantity_to_add = max_quantity # max_quantity is not capped [quantity_to_add, max_quantity_to_add] end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 9dc0a39dac..5b617e7db6 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -288,33 +288,32 @@ feature "As a consumer I want to shop with a distributor", js: true do # Update amount available in product list # If amount falls to zero, variant should be greyed out and input disabled page.should have_selector "#variant-#{variant.id}.out-of-stock" - page.should have_selector "#variants_#{variant.id}_max[max='0']" page.should have_selector "#variants_#{variant.id}_max[disabled='disabled']" end end context "when the update is for another product" do it "updates quantity" do - fill_in "variants[#{variant.id}]", with: '1' + fill_in "variants[#{variant.id}]", with: '2' wait_until { !cart_dirty } - variant.update_attributes! on_hand: 0 + variant.update_attributes! on_hand: 1 fill_in "variants[#{variant2.id}]", with: '1' wait_until { !cart_dirty } within(".out-of-stock-modal") do page.should have_content "stock levels for one or more of the products in your cart have reduced" - page.should have_content "#{product.name} - #{variant.unit_to_display} is now out of stock." + page.should have_content "#{product.name} - #{variant.unit_to_display} now only has 1 remaining" end end context "group buy products" do let(:product) { create(:simple_product, group_buy: true) } - it "updates max_quantity" do - fill_in "variants[#{variant.id}]", with: '1' - fill_in "variant_attributes[#{variant.id}][max_quantity]", with: '2' + it "does not update max_quantity" do + fill_in "variants[#{variant.id}]", with: '2' + fill_in "variant_attributes[#{variant.id}][max_quantity]", with: '3' wait_until { !cart_dirty } variant.update_attributes! on_hand: 1 @@ -325,6 +324,9 @@ feature "As a consumer I want to shop with a distributor", js: true do page.should have_content "stock levels for one or more of the products in your cart have reduced" page.should have_content "#{product.name} - #{variant.unit_to_display} now only has 1 remaining" end + + page.should have_field "variants[#{variant.id}]", with: '1' + page.should have_field "variant_attributes[#{variant.id}][max_quantity]", with: '3' end end end diff --git a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee index 6df74e3ede..d985904f81 100644 --- a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee @@ -159,12 +159,12 @@ describe 'Cart service', -> expect(li.quantity).toEqual 5 expect(li.max_quantity).toBeUndefined() - it "reduces the max_quantity in the cart", -> + it "does not reduce the max_quantity in the cart", -> li = {variant: {id: 1}, quantity: 6, max_quantity: 7} stockLevels = {1: {quantity: 5, max_quantity: 5, on_hand: 5}} spyOn(Cart, 'line_items_present').andReturn [li] Cart.compareAndNotifyStockLevels stockLevels - expect(li.max_quantity).toEqual 5 + expect(li.max_quantity).toEqual 7 it "resets the count on hand available", -> li = {variant: {id: 1}, quantity: 6} diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index 2b5362e94f..111108f7b4 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -55,9 +55,9 @@ module Spree li.reload.quantity.should == 5 end - it "caps max_quantity" do + it "does not cap max_quantity" do li.cap_quantity_at_stock! - li.reload.max_quantity.should == 5 + li.reload.max_quantity.should == 10 end it "works for products without max_quantity" do diff --git a/spec/models/spree/order_populator_spec.rb b/spec/models/spree/order_populator_spec.rb index 4a50e59fa8..f0bbc81f55 100644 --- a/spec/models/spree/order_populator_spec.rb +++ b/spec/models/spree/order_populator_spec.rb @@ -213,8 +213,8 @@ module Spree op.quantities_to_add(v, 5, 6).should == [5, 6] end - it "returns a limited amount when not entirely available" do - op.quantities_to_add(v, 15, 16).should == [10, 10] + it "also returns the full amount when not entirely available" do + op.quantities_to_add(v, 15, 16).should == [10, 16] end end end From cf40e0432abde6c54c72fbd9c5a437492deaffa2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 Apr 2016 12:22:51 +1000 Subject: [PATCH 037/101] When cart is updated with insufficient stock, show amount in cart, not amount entered --- .../spree/orders_controller_decorator.rb | 37 ++++++++++++++ app/views/spree/orders/_line_item.html.haml | 2 +- spec/features/consumer/shopping/cart_spec.rb | 50 +++++++++++++------ 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index e675ce7e94..2c0ff8562c 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -16,6 +16,7 @@ Spree::OrdersController.class_eval do # Patching to redirect to shop if order is empty def edit @order = current_order(true) + @insufficient_stock_lines = @order.insufficient_stock_lines if @order.line_items.empty? redirect_to main_app.shop_path @@ -28,6 +29,41 @@ Spree::OrdersController.class_eval do end end + + def update + @insufficient_stock_lines = [] + @order = current_order + unless @order + flash[:error] = t(:order_not_found) + redirect_to root_path and return + end + + if @order.update_attributes(params[:order]) + @order.line_items = @order.line_items.select {|li| li.quantity > 0 } + @order.restart_checkout_flow + + render :edit and return unless apply_coupon_code + + fire_event('spree.order.contents_changed') + respond_with(@order) do |format| + format.html do + if params.has_key?(:checkout) + @order.next_transition.run_callbacks if @order.cart? + redirect_to checkout_state_path(@order.checkout_steps.first) + else + redirect_to cart_path + end + end + end + else + # Show order with original values, not newly entered ones + @insufficient_stock_lines = @order.insufficient_stock_lines + @order.line_items(true) + respond_with(@order) + end + end + + def populate # Without intervention, the Spree::Adjustment#update_adjustable callback is called many times # during cart population, for both taxation and enterprise fees. This operation triggers a @@ -55,6 +91,7 @@ Spree::OrdersController.class_eval do end end + # Report the stock levels in the order for all variant ids requested def stock_levels(order, variant_ids) stock_levels = li_stock_levels(order) diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index 6014b617a9..f4415199a4 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -17,7 +17,7 @@ = render 'spree/shared/line_item_name', line_item: line_item - - if @order.insufficient_stock_lines.include? line_item + - if @insufficient_stock_lines.include? line_item %span.out-of-stock = variant.in_stock? ? t(:insufficient_stock, :on_hand => variant.on_hand) : t(:out_of_stock) %br/ diff --git a/spec/features/consumer/shopping/cart_spec.rb b/spec/features/consumer/shopping/cart_spec.rb index a80fd908ff..5097cb50b8 100644 --- a/spec/features/consumer/shopping/cart_spec.rb +++ b/spec/features/consumer/shopping/cart_spec.rb @@ -7,25 +7,45 @@ feature "full-page cart", js: true do include UIComponentHelper describe "viewing the cart" do + let!(:zone) { create(:zone_with_member) } + let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } + let(:supplier) { create(:supplier_enterprise) } + let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } + let(:enterprise_fee) { create(:enterprise_fee, amount: 11.00, tax_category: product.tax_category) } + let(:product) { create(:taxed_product, supplier: supplier, zone: zone, price: 110.00, tax_rate_amount: 0.1) } + let(:variant) { product.variants.first } + let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } + + before do + add_enterprise_fee enterprise_fee + set_order order + add_product_to_cart + visit spree.cart_path + end + describe "tax" do - let!(:zone) { create(:zone_with_member) } - let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } - let(:supplier) { create(:supplier_enterprise) } - let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } - let(:enterprise_fee) { create(:enterprise_fee, amount: 11.00, tax_category: product.tax_category) } - let(:product) { create(:taxed_product, supplier: supplier, zone: zone, price: 110.00, tax_rate_amount: 0.1) } - let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } - - before do - add_enterprise_fee enterprise_fee - set_order order - add_product_to_cart - visit spree.cart_path - end - it "shows the total tax for the order, including product tax and tax on fees" do page.should have_selector '.tax-total', text: '11.00' # 10 + 1 end end + + describe "updating quantities with insufficient stock available" do + let(:li) { order.line_items(true).last } + + use_short_wait 10 + + before do + variant.update_attributes! on_hand: 2 + end + + it "shows the quantities saved, not those submitted" do + fill_in "order_line_items_attributes_0_quantity", with: '4' + + click_button 'Update' + + page.should have_field "order[line_items_attributes][0][quantity]", with: '1' + page.should have_content "Insufficient stock available, only 2 remaining" + end + end end end From 36f4df29315f9bfd3338a4449f2704ddb4b3308a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 Apr 2016 12:27:20 +1000 Subject: [PATCH 038/101] Allow max value in cart of what's on hand --- app/views/spree/orders/_line_item.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index f4415199a4..018cd9fa55 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -30,7 +30,7 @@ -# "price-breakdown-placement" => "left", -# "price-breakdown-animation" => true} %td.text-center.cart-item-quantity{"data-hook" => "cart_item_quantity"} - = item_form.number_field :quantity, :min => 0, :class => "line_item_quantity", :size => 5 + = item_form.number_field :quantity, :min => 0, :max => variant.on_hand, :class => "line_item_quantity", :size => 5 %td.cart-item-total.text-right{"data-hook" => "cart_item_total"} = line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil? From daa5b00a2aa2851bc0a782bea854b3893087af63 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 15 Apr 2016 12:22:17 +1000 Subject: [PATCH 039/101] Uses openstreetmap tiles --- .../darkswarm/directives/map_search.js.coffee | 15 +++++++++++++++ .../services/map_configuration.js.coffee | 9 ++++++--- app/assets/stylesheets/darkswarm/map.css.sass | 13 +++++++++++++ app/views/map/index.html.haml | 5 +++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee index f67407ffb3..20a2cba1d4 100644 --- a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee @@ -7,6 +7,21 @@ Darkswarm.directive 'mapSearch', ($timeout)-> link: (scope, elem, attrs, ctrl)-> $timeout => map = ctrl.getMap() + + map.mapTypes.set 'OSM', new (google.maps.ImageMapType)( + getTileUrl: (coord, zoom) -> + # "Wrap" x (logitude) at 180th meridian properly + # NB: Don't touch coord.x because coord param is by reference, and changing its x property breakes something in Google's lib + tilesPerGlobe = 1 << zoom + x = coord.x % tilesPerGlobe + if x < 0 + x = tilesPerGlobe + x + # Wrap y (latitude) in a like manner if you want to enable vertical infinite scroll + 'http://tile.openstreetmap.org/' + zoom + '/' + x + '/' + coord.y + '.png' + tileSize: new (google.maps.Size)(256, 256) + name: 'OpenStreetMap' + maxZoom: 18) + input = (document.getElementById("pac-input")) map.controls[google.maps.ControlPosition.TOP_LEFT].push input searchBox = new google.maps.places.SearchBox((input)) diff --git a/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee b/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee index 9c5a375d8c..992872e2da 100644 --- a/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee @@ -1,11 +1,14 @@ Darkswarm.factory "MapConfiguration", -> new class MapConfiguration options: - center: + center: latitude: -37.4713077 longitude: 144.7851531 zoom: 12 - additional_options: {} - #mapTypeId: 'satellite' + additional_options: + #mapTypeId: 'satellite' + mapTypeId: 'OSM' + mapTypeControl: false + streetViewControl: false styles: [{"featureType":"landscape","stylers":[{"saturation":-100},{"lightness":65},{"visibility":"on"}]},{"featureType":"poi","stylers":[{"saturation":-100},{"lightness":51},{"visibility":"simplified"}]},{"featureType":"road.highway","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"road.arterial","stylers":[{"saturation":-100},{"lightness":30},{"visibility":"on"}]},{"featureType":"road.local","stylers":[{"saturation":-100},{"lightness":40},{"visibility":"on"}]},{"featureType":"transit","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"administrative.province","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"labels","stylers":[{"visibility":"on"},{"lightness":-25},{"saturation":-100}]},{"featureType":"water","elementType":"geometry","stylers":[{"hue":"#ffff00"},{"lightness":-25},{"saturation":-97}]},{"featureType":"road","elementType": "labels.icon","stylers":[{"visibility":"off"}]}] diff --git a/app/assets/stylesheets/darkswarm/map.css.sass b/app/assets/stylesheets/darkswarm/map.css.sass index a831e9448c..8a63803816 100644 --- a/app/assets/stylesheets/darkswarm/map.css.sass +++ b/app/assets/stylesheets/darkswarm/map.css.sass @@ -26,3 +26,16 @@ width: 80% &:active, &:focus, &.active background: rgba(255,255,255, 1) + +.map-footer + position: fixed + font-size: x-small + left: 0 + right: 0 + bottom: 0 + width: 100% + height: 23px + margin: 0 + padding: 6px + z-index: 2 + background: WHITE diff --git a/app/views/map/index.html.haml b/app/views/map/index.html.haml index a4d012282b..30948b8724 100644 --- a/app/views/map/index.html.haml +++ b/app/views/map/index.html.haml @@ -9,3 +9,8 @@ %map-search %markers{models: "OfnMap.enterprises", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} + +.map-footer + \© + %a{:href => "http://www.openstreetmap.org/copyright"} OpenStreetMap + contributors From 824a29624fbba3bf270e8fa5f17a6d4a2c50e146 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 27 Apr 2016 14:17:29 +1000 Subject: [PATCH 040/101] Tweak the map UI --- .../javascripts/darkswarm/directives/map_search.js.coffee | 3 ++- .../javascripts/darkswarm/services/map_configuration.js.coffee | 2 +- app/assets/stylesheets/darkswarm/map.css.sass | 3 ++- app/views/map/index.html.haml | 3 +-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee index 20a2cba1d4..e67ce529a5 100644 --- a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee @@ -8,6 +8,7 @@ Darkswarm.directive 'mapSearch', ($timeout)-> $timeout => map = ctrl.getMap() + # Use OSM tiles server map.mapTypes.set 'OSM', new (google.maps.ImageMapType)( getTileUrl: (coord, zoom) -> # "Wrap" x (logitude) at 180th meridian properly @@ -36,7 +37,7 @@ Darkswarm.directive 'mapSearch', ($timeout)-> #map.setCenter place.geometry.location map.fitBounds place.geometry.viewport #map.fitBounds bounds - + # Bias the SearchBox results towards places that are within the bounds of the # current map's viewport. google.maps.event.addListener map, "bounds_changed", -> diff --git a/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee b/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee index 992872e2da..1979affeab 100644 --- a/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee @@ -6,7 +6,7 @@ Darkswarm.factory "MapConfiguration", -> longitude: 144.7851531 zoom: 12 additional_options: - #mapTypeId: 'satellite' + # mapTypeId: 'satellite' mapTypeId: 'OSM' mapTypeControl: false streetViewControl: false diff --git a/app/assets/stylesheets/darkswarm/map.css.sass b/app/assets/stylesheets/darkswarm/map.css.sass index 8a63803816..4692bdc1b4 100644 --- a/app/assets/stylesheets/darkswarm/map.css.sass +++ b/app/assets/stylesheets/darkswarm/map.css.sass @@ -22,6 +22,7 @@ background: rgba(255,255,255,0.85) width: 50% margin-top: 1.2rem + margin-left: 1rem @media all and (max-width: 768px) width: 80% &:active, &:focus, &.active @@ -38,4 +39,4 @@ margin: 0 padding: 6px z-index: 2 - background: WHITE + background: #fff diff --git a/app/views/map/index.html.haml b/app/views/map/index.html.haml index 30948b8724..97f4eb602d 100644 --- a/app/views/map/index.html.haml +++ b/app/views/map/index.html.haml @@ -12,5 +12,4 @@ .map-footer \© - %a{:href => "http://www.openstreetmap.org/copyright"} OpenStreetMap - contributors + %a{:href => "http://www.openstreetmap.org/copyright"} OpenStreetMap contributors From f691636c757b452f6a173d08e38e5375ec1edcd2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 Apr 2016 14:47:45 +1000 Subject: [PATCH 041/101] Fix spec --- spec/controllers/spree/orders_controller_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index 286bb9217a..74e9efaaf9 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -15,6 +15,7 @@ describe Spree::OrdersController do controller.stub(:current_order_cycle).and_return(order_cycle) controller.stub(:current_order).and_return order order.stub_chain(:line_items, :empty?).and_return true + order.stub(:insufficient_stock_lines).and_return [] session[:access_token] = order.token spree_get :edit response.should redirect_to shop_path From 1220ff8a068ff21fce960f06bf04d755c3fd0986 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 Apr 2016 15:05:44 +1000 Subject: [PATCH 042/101] Notify when stock limit reached on shopfront rather than silently capping --- .../darkswarm/directives/on_hand.js.coffee | 9 +++++++++ .../partials/shop_variant_no_group_buy.html.haml | 2 +- .../partials/shop_variant_with_group_buy.html.haml | 2 +- spec/features/consumer/shopping/shopping_spec.rb | 11 +++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/directives/on_hand.js.coffee diff --git a/app/assets/javascripts/darkswarm/directives/on_hand.js.coffee b/app/assets/javascripts/darkswarm/directives/on_hand.js.coffee new file mode 100644 index 0000000000..0e3a96c608 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/on_hand.js.coffee @@ -0,0 +1,9 @@ +Darkswarm.directive "ofnOnHand", -> + restrict: 'A' + link: (scope, elem, attr) -> + on_hand = parseInt(attr.ofnOnHand) + elem.bind 'change', (e) -> + if parseInt(elem.val()) > on_hand + scope.$apply -> + alert t('insufficient_stock', {on_hand: on_hand}) + elem.val(on_hand) diff --git a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml index 36a4e44ade..1153d93b0f 100644 --- a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml +++ b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml @@ -7,6 +7,6 @@ placeholder: "0", "ofn-disable-scroll" => true, "ng-model" => "variant.line_item.quantity", - max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", + "ofn-on-hand" => "{{variant.on_demand && 9999 || variant.count_on_hand }}", "ng-disabled" => "!variant.on_demand && variant.count_on_hand == 0", name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} diff --git a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml index f2b0b95837..6601f0c019 100644 --- a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml +++ b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml @@ -8,7 +8,7 @@ "ng-model" => "variant.line_item.quantity", placeholder: "{{'shop_variant_quantity_min' | t}}", "ofn-disable-scroll" => true, - max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", + "ofn-on-hand" => "{{variant.on_demand && 9999 || variant.count_on_hand }}", name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} %span.bulk-input %input.bulk.second{type: :number, diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 5b617e7db6..2ca2d63341 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -238,6 +238,17 @@ feature "As a consumer I want to shop with a distributor", js: true do Spree::LineItem.where(id: li).should be_empty end + it "alerts us when we enter a quantity greater than the stock available" do + variant.update_attributes on_hand: 5 + visit shop_path + + accept_alert 'Insufficient stock available, only 5 remaining' do + fill_in "variants[#{variant.id}]", with: '10' + end + + page.should have_field "variants[#{variant.id}]", with: '5' + end + describe "when a product goes out of stock just before it's added to the cart" do it "stops the attempt, shows an error message and refreshes the products asynchronously" do variant.update_attributes! on_hand: 0 From 1384140e41b4266caa8e933d3af4d895357fa4e4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 Apr 2016 15:10:54 +1000 Subject: [PATCH 043/101] Notify when stock limit reached on cart rather than silently capping --- app/views/spree/orders/_line_item.html.haml | 2 +- spec/features/consumer/shopping/cart_spec.rb | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index 018cd9fa55..3897654467 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -30,7 +30,7 @@ -# "price-breakdown-placement" => "left", -# "price-breakdown-animation" => true} %td.text-center.cart-item-quantity{"data-hook" => "cart_item_quantity"} - = item_form.number_field :quantity, :min => 0, :max => variant.on_hand, :class => "line_item_quantity", :size => 5 + = item_form.number_field :quantity, :min => 0, "ofn-on-hand" => variant.on_hand, :class => "line_item_quantity", :size => 5 %td.cart-item-total.text-right{"data-hook" => "cart_item_total"} = line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil? diff --git a/spec/features/consumer/shopping/cart_spec.rb b/spec/features/consumer/shopping/cart_spec.rb index 5097cb50b8..ed573d0460 100644 --- a/spec/features/consumer/shopping/cart_spec.rb +++ b/spec/features/consumer/shopping/cart_spec.rb @@ -32,12 +32,20 @@ feature "full-page cart", js: true do describe "updating quantities with insufficient stock available" do let(:li) { order.line_items(true).last } - use_short_wait 10 - before do variant.update_attributes! on_hand: 2 end + it "prevents me from entering an invalid value" do + visit spree.cart_path + + accept_alert 'Insufficient stock available, only 2 remaining' do + fill_in "order_line_items_attributes_0_quantity", with: '4' + end + + page.should have_field "order_line_items_attributes_0_quantity", with: '2' + end + it "shows the quantities saved, not those submitted" do fill_in "order_line_items_attributes_0_quantity", with: '4' From 8996acf314adc698aa36faa9c946126541813a0d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 27 Apr 2016 15:25:05 +1000 Subject: [PATCH 044/101] Fix spec --- spec/features/consumer/shopping/shopping_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 2ca2d63341..dceecd43f8 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -270,7 +270,7 @@ feature "As a consumer I want to shop with a distributor", js: true do # Update amount available in product list # If amount falls to zero, variant should be greyed out and input disabled page.should have_selector "#variant-#{variant.id}.out-of-stock" - page.should have_selector "#variants_#{variant.id}[max='0']" + page.should have_selector "#variants_#{variant.id}[ofn-on-hand='0']" page.should have_selector "#variants_#{variant.id}[disabled='disabled']" end From 65895752dacf1541e25f969867a713067e1ee5c0 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Apr 2016 11:49:30 +1000 Subject: [PATCH 045/101] Remove cruft --- app/assets/javascripts/admin/directives/track_master.js.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/directives/track_master.js.coffee b/app/assets/javascripts/admin/directives/track_master.js.coffee index ce26a4aa32..be84e0b204 100644 --- a/app/assets/javascripts/admin/directives/track_master.js.coffee +++ b/app/assets/javascripts/admin/directives/track_master.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").directive "ofnTrackMaster", ["DirtyProducts", (DirtyProducts) -> +angular.module("ofn.admin").directive "ofnTrackMaster", (DirtyProducts) -> require: "ngModel" link: (scope, element, attrs, ngModel) -> ngModel.$parsers.push (viewValue) -> @@ -6,4 +6,3 @@ angular.module("ofn.admin").directive "ofnTrackMaster", ["DirtyProducts", (Dirty DirtyProducts.addMasterProperty scope.product.id, scope.product.master.id, attrs.ofnTrackMaster, viewValue scope.displayDirtyProducts() viewValue - ] \ No newline at end of file From 88e9eb59cf5de3453965bca310bd72dddcaf8a40 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Apr 2016 11:56:48 +1000 Subject: [PATCH 046/101] Do not allow invalid quantity to reach model, triggering server update --- .../darkswarm/directives/on_hand.js.coffee | 24 +++++++++++++------ app/views/spree/orders/_line_item.html.haml | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/darkswarm/directives/on_hand.js.coffee b/app/assets/javascripts/darkswarm/directives/on_hand.js.coffee index 0e3a96c608..610b11d3ca 100644 --- a/app/assets/javascripts/darkswarm/directives/on_hand.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/on_hand.js.coffee @@ -1,9 +1,19 @@ Darkswarm.directive "ofnOnHand", -> restrict: 'A' - link: (scope, elem, attr) -> - on_hand = parseInt(attr.ofnOnHand) - elem.bind 'change', (e) -> - if parseInt(elem.val()) > on_hand - scope.$apply -> - alert t('insufficient_stock', {on_hand: on_hand}) - elem.val(on_hand) + require: "ngModel" + + link: (scope, elem, attr, ngModel) -> + # In cases where this field gets its value from the HTML element rather than the model, + # initialise the model with the HTML value. + if scope.$eval(attr.ngModel) == undefined + ngModel.$setViewValue elem.val() + + ngModel.$parsers.push (viewValue) -> + on_hand = parseInt(attr.ofnOnHand) + if parseInt(viewValue) > on_hand + alert t('insufficient_stock', {on_hand: on_hand}) + viewValue = on_hand + ngModel.$setViewValue viewValue + ngModel.$render() + + viewValue diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index 3897654467..5976332a18 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -30,7 +30,7 @@ -# "price-breakdown-placement" => "left", -# "price-breakdown-animation" => true} %td.text-center.cart-item-quantity{"data-hook" => "cart_item_quantity"} - = item_form.number_field :quantity, :min => 0, "ofn-on-hand" => variant.on_hand, :class => "line_item_quantity", :size => 5 + = item_form.number_field :quantity, :min => 0, "ofn-on-hand" => variant.on_hand, "ng-model" => "line_item_#{line_item.id}", :class => "line_item_quantity", :size => 5 %td.cart-item-total.text-right{"data-hook" => "cart_item_total"} = line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil? From 36a4aab020edc8c968e2f3754bc07854df9b01b8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Apr 2016 15:07:55 +1000 Subject: [PATCH 047/101] Adjust styling to blend --- app/assets/stylesheets/darkswarm/map.css.sass | 20 +++++++++++++------ app/views/map/index.html.haml | 3 +-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/map.css.sass b/app/assets/stylesheets/darkswarm/map.css.sass index 4692bdc1b4..e2a25d61c9 100644 --- a/app/assets/stylesheets/darkswarm/map.css.sass +++ b/app/assets/stylesheets/darkswarm/map.css.sass @@ -30,13 +30,21 @@ .map-footer position: fixed - font-size: x-small - left: 0 - right: 0 - bottom: 0 + z-index: 2 width: 100% height: 23px + left: 80px + right: 0 + bottom: 6px margin: 0 padding: 6px - z-index: 2 - background: #fff + font-size: 14px + font-weight: bold + text-shadow: 2px 2px #aaa + color: #fff + + a, a:hover, a:active, a:focus + color: #fff + + @media all and (max-width: 1025px) + left: 0px \ No newline at end of file diff --git a/app/views/map/index.html.haml b/app/views/map/index.html.haml index 97f4eb602d..e4a22e540c 100644 --- a/app/views/map/index.html.haml +++ b/app/views/map/index.html.haml @@ -11,5 +11,4 @@ coords: "'self'", icon: "'icon'", click: "'reveal'"} .map-footer - \© - %a{:href => "http://www.openstreetmap.org/copyright"} OpenStreetMap contributors + %a{:href => "http://www.openstreetmap.org/copyright"} © OpenStreetMap contributors From fa5fa9e2286554617996182424056f9f29569edd Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 29 Apr 2016 12:33:33 +1000 Subject: [PATCH 048/101] Auto-complete tags on customers page - new controller serving tags for an enterprise as JSON - customers page suggesting these tags - emphasising tags that have rules --- .../customers_controller.js.coffee | 12 +++++++- .../services/tags_resource.js.coffee | 9 ++++++ .../admin/filters/translate.js.coffee | 7 ----- .../shipping_methods.js.coffee | 2 +- .../tags_with_translation.js.coffee | 3 +- .../admin/utils/filters/translate.js.coffee | 7 +++++ .../javascripts/templates/admin/tag.html.haml | 8 +++++ .../admin/tag_autocomplete.html.haml | 11 +++++++ .../templates/admin/tags_input.html.haml | 7 +++++ .../stylesheets/admin/customers.css.scss | 3 ++ app/controllers/admin/tags_controller.rb | 28 ++++++++++++++++++ app/models/enterprise.rb | 14 +++++++++ app/models/spree/ability_decorator.rb | 9 ++---- .../api/admin/customer_serializer.rb | 6 +++- app/views/admin/customers/index.html.haml | 2 +- config/locales/en.yml | 4 +++ config/routes.rb | 2 ++ .../customers_controller_spec.js.coffee | 29 +++++++++++++++++++ .../admin/customer_serializer_spec.rb | 14 +++++++++ 19 files changed, 159 insertions(+), 18 deletions(-) create mode 100644 app/assets/javascripts/admin/customers/services/tags_resource.js.coffee delete mode 100644 app/assets/javascripts/admin/filters/translate.js.coffee create mode 100644 app/assets/javascripts/admin/utils/filters/translate.js.coffee create mode 100644 app/assets/javascripts/templates/admin/tag.html.haml create mode 100644 app/assets/javascripts/templates/admin/tag_autocomplete.html.haml create mode 100644 app/assets/javascripts/templates/admin/tags_input.html.haml create mode 100644 app/assets/stylesheets/admin/customers.css.scss create mode 100644 app/controllers/admin/tags_controller.rb create mode 100644 spec/serializers/admin/customer_serializer_spec.rb 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 d2e6d58562..bfccfd3f4b 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, CustomerResource, Columns, pendingChanges, shops) -> +angular.module("admin.customers").controller "customersCtrl", ($scope, CustomerResource, TagsResource, $q, Columns, pendingChanges, shops) -> $scope.shop = {} $scope.shops = shops $scope.submitAll = pendingChanges.submitAll @@ -12,6 +12,16 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, CustomerR if $scope.shop.id? $scope.customers = index {enterprise_id: $scope.shop.id} + $scope.findTags = (query) -> + defer = $q.defer() + params = + enterprise_id: $scope.shop.id + TagsResource.index params, (data) => + filtered = data.filter (tag) -> + tag.text.toLowerCase().indexOf(query.toLowerCase()) != -1 + defer.resolve filtered + defer.promise + $scope.add = (email) -> params = enterprise_id: $scope.shop.id diff --git a/app/assets/javascripts/admin/customers/services/tags_resource.js.coffee b/app/assets/javascripts/admin/customers/services/tags_resource.js.coffee new file mode 100644 index 0000000000..ad89511e32 --- /dev/null +++ b/app/assets/javascripts/admin/customers/services/tags_resource.js.coffee @@ -0,0 +1,9 @@ +angular.module("admin.customers").factory 'TagsResource', ($resource) -> + $resource('/admin/tags.json', {}, { + 'index': + method: 'GET' + isArray: true + cache: true + params: + enterprise_id: '@enterprise_id' + }) diff --git a/app/assets/javascripts/admin/filters/translate.js.coffee b/app/assets/javascripts/admin/filters/translate.js.coffee deleted file mode 100644 index 20becc147a..0000000000 --- a/app/assets/javascripts/admin/filters/translate.js.coffee +++ /dev/null @@ -1,7 +0,0 @@ -angular.module('ofn.admin').filter "translate", -> - (key, options) -> - t(key, options) - -angular.module('ofn.admin').filter "t", -> - (key, options) -> - t(key, options) 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 232eee7045..863163a9ef 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", ["ngTagsInput", 'admin.utils']) +angular.module("admin.shippingMethods", ["ngTagsInput", 'admin.utils', 'templates']) 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 6ce7953608..7a6ae9afff 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,10 +1,11 @@ angular.module("admin.utils").directive "tagsWithTranslation", ($timeout) -> restrict: "E" - template: "" + templateUrl: "admin/tags_input.html" scope: object: "=" tagsAttr: "@?" tagListAttr: "@?" + findTags: "&" link: (scope, element, attrs) -> $timeout -> scope.tagsAttr ||= "tags" diff --git a/app/assets/javascripts/admin/utils/filters/translate.js.coffee b/app/assets/javascripts/admin/utils/filters/translate.js.coffee new file mode 100644 index 0000000000..1a0602a59d --- /dev/null +++ b/app/assets/javascripts/admin/utils/filters/translate.js.coffee @@ -0,0 +1,7 @@ +angular.module("admin.utils").filter "translate", -> + (key, options) -> + t(key, options) + +angular.module("admin.utils").filter "t", -> + (key, options) -> + t(key, options) diff --git a/app/assets/javascripts/templates/admin/tag.html.haml b/app/assets/javascripts/templates/admin/tag.html.haml new file mode 100644 index 0000000000..c4afcfa5ad --- /dev/null +++ b/app/assets/javascripts/templates/admin/tag.html.haml @@ -0,0 +1,8 @@ +.tag-template + %div + %span.tag-with-rules{ ng: { if: "data.rules" }, "ofn-with-tip" => "{{ 'admin.tag_has_rules' | t:{num: data.rules} }}" } + {{$getDisplayText()}} + %span{ ng: { if: "!data.rules" } } + {{$getDisplayText()}} + %a.remove-button{ ng: {click: "$removeTag()"} } + ✖ diff --git a/app/assets/javascripts/templates/admin/tag_autocomplete.html.haml b/app/assets/javascripts/templates/admin/tag_autocomplete.html.haml new file mode 100644 index 0000000000..49ff00ff27 --- /dev/null +++ b/app/assets/javascripts/templates/admin/tag_autocomplete.html.haml @@ -0,0 +1,11 @@ +.autocomplete-template + %span.tag-with-rules{ ng: { if: "data.rules" } } + {{$getDisplayText()}} + %span.tag-with-rules{ ng: { if: "data.rules == 1" } } + — + = t 'admin.has_one_rule' + %span.tag-with-rules{ ng: { if: "data.rules > 1" } } + — + = t 'admin.has_n_rules', { num: '{{data.rules}}' } + %span{ ng: { if: "!data.rules" } } + {{$getDisplayText()}} diff --git a/app/assets/javascripts/templates/admin/tags_input.html.haml b/app/assets/javascripts/templates/admin/tags_input.html.haml new file mode 100644 index 0000000000..2dd75ce5ac --- /dev/null +++ b/app/assets/javascripts/templates/admin/tags_input.html.haml @@ -0,0 +1,7 @@ +%tags-input{ template: 'admin/tag.html', ng: { model: 'object[tagsAttr]' } } + %auto-complete{source: "findTags({query: $query})", + template: "admin/tag_autocomplete.html", + "min-length" => "0", + "load-on-focus" => "true", + "load-on-empty" => "true", + "max-results-to-show" => "32"} diff --git a/app/assets/stylesheets/admin/customers.css.scss b/app/assets/stylesheets/admin/customers.css.scss new file mode 100644 index 0000000000..e3c427649c --- /dev/null +++ b/app/assets/stylesheets/admin/customers.css.scss @@ -0,0 +1,3 @@ +.tag-with-rules { + color: black; +} diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb new file mode 100644 index 0000000000..3ab5685ffe --- /dev/null +++ b/app/controllers/admin/tags_controller.rb @@ -0,0 +1,28 @@ +module Admin + class TagsController < Spree::Admin::BaseController + respond_to :json + + def index + respond_to do |format| + format.json do + serialiser = ActiveModel::ArraySerializer.new(tags_of_enterprise) + render json: serialiser.to_json + end + end + end + + private + + def enterprise + Enterprise.managed_by(spree_current_user).find_by_id(params[:enterprise_id]) + end + + def tags_of_enterprise + return [] unless enterprise + tag_rule_map = enterprise.rules_per_tag + tag_rule_map.keys.map do |tag| + { text: tag, rules: tag_rule_map[tag] } + end + end + end +end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index dfc5050a38..d3e86ff3ca 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -352,6 +352,20 @@ class Enterprise < ActiveRecord::Base end end + def rules_per_tag + tag_rule_map = {} + tag_rules.each do |rule| + rule.preferred_customer_tags.split(",").each do |tag| + if tag_rule_map[tag] + tag_rule_map[tag] += 1 + else + tag_rule_map[tag] = 1 + end + end + end + tag_rule_map + end + protected def devise_mailer diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 867d3c9e2b..e250d1341d 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -101,11 +101,6 @@ 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) @@ -221,7 +216,9 @@ class AbilityDecorator # Reports page can [:admin, :index, :customers, :group_buys, :bulk_coop, :sales_tax, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :xero_invoices], :report - can [:admin, :index, :update], Customer, enterprise_id: Enterprise.managed_by(user).pluck(:id) + can [:create], Customer + can [:admin, :index, :update, :destroy], Customer, enterprise_id: Enterprise.managed_by(user).pluck(:id) + can [:admin, :index], :tag end diff --git a/app/serializers/api/admin/customer_serializer.rb b/app/serializers/api/admin/customer_serializer.rb index 3cb9518a9f..052f49a917 100644 --- a/app/serializers/api/admin/customer_serializer.rb +++ b/app/serializers/api/admin/customer_serializer.rb @@ -6,6 +6,10 @@ class Api::Admin::CustomerSerializer < ActiveModel::Serializer end def tags - object.tag_list.map{ |t| { text: t } } + tag_rule_map = object.enterprise.rules_per_tag + object.tag_list.map do |tag| + { text: tag, rules: tag_rule_map[tag] } + end end + end diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index e33b871df4..0a348d4a0a 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -56,7 +56,7 @@ %input{ :type => 'text', :name => 'code', :id => 'code', 'ng-model' => 'customer.code', 'obj-for-update' => "customer", "attr-for-update" => "code" } %td.tags{ 'ng-show' => 'columns.tags.visible' } .tag_watcher{ 'obj-for-update' => "customer", "attr-for-update" => "tag_list"} - %tags_with_translation{ object: 'customer' } + %tags_with_translation{ object: 'customer', 'find-tags' => 'findTags(query)' } %td.actions %a{ 'ng-click' => "deleteCustomer(customer)", :class => "delete-customer icon-trash no-text" } %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'submitAll()' } diff --git a/config/locales/en.yml b/config/locales/en.yml index 7b5f9b168f..e9de6c9abd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -80,6 +80,10 @@ en: whats_this: What's this? + tag_has_rules: "Existing rules for this tag: %{num}" + has_one_rule: "has one rule" + has_n_rules: "has %{num} rules" + customers: index: add_customer: "Add customer" diff --git a/config/routes.rb b/config/routes.rb index 8d8d6d27c3..e240f901a8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -117,6 +117,8 @@ Openfoodnetwork::Application.routes.draw do resources :customers, only: [:index, :create, :update, :destroy] + resources :tags, only: [:index], format: :json + resource :content resource :accounts_and_billing_settings, only: [:edit, :update] do 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 215f2834ed..6981ef4e4f 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 @@ -47,3 +47,32 @@ describe "CustomersCtrl", -> http.flush() expect(scope.customers.length).toBe 1 expect(scope.customers[0]).not.toAngularEqual customer + + describe "scope.findTags", -> + tags = [ + { text: 'one' } + { text: 'two' } + { text: 'three' } + ] + beforeEach -> + http.expectGET('/admin/tags.json?enterprise_id=1').respond 200, tags + + it "retrieves the tag list", -> + promise = scope.findTags('') + result = null + promise.then (data) -> + result = data + http.flush() + expect(result).toAngularEqual tags + + it "filters the tag list", -> + filtered_tags = [ + { text: 'two' } + { text: 'three' } + ] + promise = scope.findTags('t') + result = null + promise.then (data) -> + result = data + http.flush() + expect(result).toAngularEqual filtered_tags diff --git a/spec/serializers/admin/customer_serializer_spec.rb b/spec/serializers/admin/customer_serializer_spec.rb new file mode 100644 index 0000000000..d63f00d4aa --- /dev/null +++ b/spec/serializers/admin/customer_serializer_spec.rb @@ -0,0 +1,14 @@ +describe Api::Admin::CustomerSerializer do + let(:customer) { create(:customer, tag_list: "one, two, three") } + let!(:tag_rule) { create(:tag_rule, enterprise: customer.enterprise, preferred_customer_tags: "two") } + + it "serializes a customer" do + serializer = Api::Admin::CustomerSerializer.new customer + result = JSON.parse(serializer.to_json) + expect(result['email']).to eq customer.email + tags = result['tags'] + expect(tags.length).to eq 3 + expect(tags[0]).to eq({ "text" => 'one', "rules" => nil }) + expect(tags[1]).to eq({ "text" => 'two', "rules" => 1 }) + end +end From bed7ec5953e0bc17d5fb9e18ab46a5e047913a83 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 1 May 2016 12:59:55 +0100 Subject: [PATCH 049/101] Add total to producer emails --- app/mailers/producer_mailer.rb | 5 +++++ app/views/producer_mailer/order_cycle_report.text.haml | 3 +++ spec/mailers/producer_mailer_spec.rb | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index b9fb52fb41..d65d722894 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -6,6 +6,7 @@ class ProducerMailer < Spree::BaseMailer @order_cycle = order_cycle @line_items = aggregated_line_items_from(@order_cycle, @producer) @receival_instructions = @order_cycle.receival_instructions_for @producer + @total = total_from_line_items(@line_items) subject = "[#{Spree::Config.site_name}] Order cycle report for #{producer.name}" @@ -49,4 +50,8 @@ class ProducerMailer < Spree::BaseMailer lis end end + + def total_from_line_items(aggregated_line_items) + Spree::Money.new(aggregated_line_items.values.map(&:display_amount).reduce(:+)).to_s + end end diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml index 93748395ce..ea035391b8 100644 --- a/app/views/producer_mailer/order_cycle_report.text.haml +++ b/app/views/producer_mailer/order_cycle_report.text.haml @@ -15,6 +15,9 @@ Here is a summary of the orders for your products: - @line_items.each_pair do |variant, line_item| #{variant.sku} - #{raw(variant.product.supplier.name)} - #{raw(variant.product_and_full_name)} (QTY: #{line_item.quantity}) @ #{line_item.single_money} = #{line_item.display_amount} \ +\ +Total: #{@total} +\ Thanks and best wishes, #{@coordinator.name} #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 0c7e82b89d..4e3fd912d2 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -62,6 +62,10 @@ describe ProducerMailer do mail.body.should_not include p3.name end + it "includes the total" do + mail.body.should include 'Total: $20.00' + end + it "sends no mail when the producer has no orders" do expect do ProducerMailer.order_cycle_report(s3, order_cycle).deliver From 9ac6de4215005889d757a0cb0d1b89a47d2ee397 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 4 May 2016 11:42:07 +1000 Subject: [PATCH 050/101] Admin can set bugherd API key --- .../general_settings_controller_decorator.rb | 15 +++++++++++++ .../spree/app_configuration_decorator.rb | 3 +++ spec/features/admin/external_services_spec.rb | 22 +++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 app/controllers/spree/admin/general_settings_controller_decorator.rb create mode 100644 spec/features/admin/external_services_spec.rb diff --git a/app/controllers/spree/admin/general_settings_controller_decorator.rb b/app/controllers/spree/admin/general_settings_controller_decorator.rb new file mode 100644 index 0000000000..603f74bf63 --- /dev/null +++ b/app/controllers/spree/admin/general_settings_controller_decorator.rb @@ -0,0 +1,15 @@ +module Spree + module Admin + GeneralSettingsController.class_eval do + end + + + module GeneralSettingsEditPreferences + def edit + super + @preferences_general << :bugherd_api_key + end + end + GeneralSettingsController.send(:prepend, GeneralSettingsEditPreferences) + end +end diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index 6ef1e7b848..c169ce0231 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -23,4 +23,7 @@ Spree::AppConfiguration.class_eval do # Monitoring preference :last_job_queue_heartbeat_at, :string, default: nil + + # External services + preference :bugherd_api_key, :string, default: nil end diff --git a/spec/features/admin/external_services_spec.rb b/spec/features/admin/external_services_spec.rb new file mode 100644 index 0000000000..ed81c8e6fa --- /dev/null +++ b/spec/features/admin/external_services_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +feature 'External services' do + include AuthenticationWorkflow + + describe "bugherd" do + before do + Spree::Config.bugherd_api_key = nil + login_to_admin_section + end + + it "lets me set an API key" do + visit spree.edit_admin_general_settings_path + + fill_in 'bugherd_api_key', with: 'abc123' + click_button 'Update' + + page.should have_content 'General Settings has been successfully updated!' + expect(Spree::Config.bugherd_api_key).to eq 'abc123' + end + end +end From a11696b85ea5dfdb682b893118a6c1fe0f66e610 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 4 May 2016 12:13:03 +1000 Subject: [PATCH 051/101] Include BugHerd script only if configured, and with configured API key --- app/views/layouts/_bugherd_script.html.haml | 14 +---- .../consumer/external_services_spec.rb | 57 +++++++++++++++++++ spec/support/request/web_helper.rb | 18 ++++++ 3 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 spec/features/consumer/external_services_spec.rb diff --git a/app/views/layouts/_bugherd_script.html.haml b/app/views/layouts/_bugherd_script.html.haml index ad6fe585f5..8a04160cfd 100644 --- a/app/views/layouts/_bugherd_script.html.haml +++ b/app/views/layouts/_bugherd_script.html.haml @@ -1,18 +1,8 @@ -- if Rails.env.staging? or Rails.env.production? +- if (Rails.env.staging? || Rails.env.production?) && Spree::Config.bugherd_api_key.present? :javascript (function (d, t) { var bh = d.createElement(t), s = d.getElementsByTagName(t)[0]; bh.type = 'text/javascript'; - bh.src = '//www.bugherd.com/sidebarv2.js?apikey=4ftxjbgwx7y6ssykayr04w'; + bh.src = '//www.bugherd.com/sidebarv2.js?apikey=#{Spree::Config.bugherd_api_key}'; s.parentNode.insertBefore(bh, s); })(document, 'script'); - - --#- elsif Rails.env.production? - -#:javascript - -#(function (d, t) { - -#var bh = d.createElement(t), s = d.getElementsByTagName(t)[0]; - -#bh.type = 'text/javascript'; - -#bh.src = '//www.bugherd.com/sidebarv2.js?apikey=xro3uv55objies58o2wrua'; - -#s.parentNode.insertBefore(bh, s); - -#})(document, 'script'); diff --git a/spec/features/consumer/external_services_spec.rb b/spec/features/consumer/external_services_spec.rb new file mode 100644 index 0000000000..28be1f8cb6 --- /dev/null +++ b/spec/features/consumer/external_services_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +feature 'External services' do + include AuthenticationWorkflow + include WebHelper + + describe "bugherd" do + describe "limiting inclusion by environment" do + before { Spree::Config.bugherd_api_key = 'abc123' } + + it "is not included in test" do + visit root_path + expect(script_content(with: 'bugherd')).to be_nil + end + + it "is not included in dev" do + Rails.env.stub(:development?) { true } + visit root_path + expect(script_content(with: 'bugherd')).to be_nil + end + + it "is included in staging" do + Rails.env.stub(:staging?) { true } + visit root_path + expect(script_content(with: 'bugherd')).not_to be_nil + end + + it "is included in production" do + Rails.env.stub(:production?) { true } + visit root_path + expect(script_content(with: 'bugherd')).not_to be_nil + end + end + + context "in an environment where BugHerd is displayed" do + before { Rails.env.stub(:staging?) { true } } + + context "when there is no API key set" do + before { Spree::Config.bugherd_api_key = nil } + + it "does not include the BugHerd script" do + visit root_path + expect(script_content(with: 'bugherd')).to be_nil + end + end + + context "when an API key is set" do + before { Spree::Config.bugherd_api_key = 'abc123' } + + it "includes the BugHerd script, with the correct API key" do + visit root_path + expect(script_content(with: 'bugherd')).to include 'abc123' + end + end + end + end +end diff --git a/spec/support/request/web_helper.rb b/spec/support/request/web_helper.rb index 30ce677c6c..70c803709e 100644 --- a/spec/support/request/web_helper.rb +++ b/spec/support/request/web_helper.rb @@ -115,6 +115,24 @@ module WebHelper DirtyFormDialog.new(page) end + # Fetch the content of a script block + # eg. script_content with: 'my-script.com' + # Returns nil if not found + # Raises an exception if multiple matching blocks are found + def script_content(opts={}) + elems = page.all('script', visible: false) + + elems = elems.to_a.select { |e| e.text(:all).include? opts[:with] } if opts[:with] + + if elems.none? + nil + elsif elems.many? + raise "Multiple results returned for script_content" + else + elems.first.text(:all) + end + end + # http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara # Do not use this without good reason. Capybara's built-in waiting is very effective. def wait_until(secs=nil) From ef0e41e624ec626c17cb17855e75cd23e0b14470 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 20 Apr 2016 15:31:11 +1000 Subject: [PATCH 052/101] Use save bar on order cycle form --- .../admin/order_cycles/controllers/edit.js.coffee | 5 +++++ app/assets/javascripts/templates/admin/save_bar.html.haml | 2 +- app/views/admin/order_cycles/edit.html.haml | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee index fd426eb455..a17f52e42c 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee @@ -12,6 +12,9 @@ angular.module('admin.orderCycles') $scope.StatusMessage = StatusMessage + $scope.$watch 'order_cycle_form.$dirty', (newValue) -> + StatusMessage.display 'notice', 'You have unsaved changes' if newValue + $scope.loaded = -> Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded @@ -81,4 +84,6 @@ angular.module('admin.orderCycles') OrderCycle.removeDistributionOfVariant(variant_id) $scope.submit = (destination) -> + StatusMessage.display 'progress', "Saving..." OrderCycle.update(destination) + this.order_cycle_form.$setPristine() diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index 452e81f6e3..ac4fa55de5 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -1,4 +1,4 @@ -#save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } } +#save-bar.animate-show{ ng: { show: 'StatusMessage.active()' } } .twelve.columns.alpha %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } {{ StatusMessage.statusMessage.text || " " }} diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index f3b0bdb721..6ecb65d87e 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -27,7 +27,9 @@ - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' -= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller} do |f| += form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f| + %save-bar{ save: "submit()", form: "order_cycle_form" } + - if order_cycles_simple_form = render 'simple_form', f: f - else From 00858656b5b4f16cf4e9d04c72529663db9704c0 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 27 Apr 2016 12:15:20 +1000 Subject: [PATCH 053/101] Extend save_bar directive to support more buttons --- .../javascripts/admin/utils/directives/save_bar.js.coffee | 1 + app/assets/javascripts/templates/admin/save_bar.html.haml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee index 13e4f84bc6..9cd25d97cd 100644 --- a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee +++ b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee @@ -3,6 +3,7 @@ angular.module("admin.utils").directive "saveBar", (StatusMessage) -> scope: save: "&" form: "=" + buttons: "=" templateUrl: "admin/save_bar.html" link: (scope, element, attrs) -> scope.StatusMessage = StatusMessage diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index ac4fa55de5..83beac81a5 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -3,4 +3,6 @@ %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } {{ StatusMessage.statusMessage.text || " " }} .four.columns.omega.text-right - %input.red{type: "button", value: "Save Changes", ng: { disabled: '!form.$dirty', click: "save()" } } + + %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "save({param: button.param})" } } + From 33d0f9fc1b2598aa27d161d17d3e763894a65adf Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 27 Apr 2016 12:16:13 +1000 Subject: [PATCH 054/101] Use save_bar on order cycle update form --- app/views/admin/order_cycles/_form.html.haml | 14 ++++++-------- .../admin/order_cycles/_simple_form.html.haml | 13 +++++-------- app/views/admin/order_cycles/edit.html.haml | 3 ++- .../admin/variant_overrides/_products.html.haml | 2 +- .../spree/admin/orders/bulk_management.html.haml | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index c447d35d58..13b317c314 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -52,14 +52,12 @@ .actions - if @order_cycle.new_record? = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - - else - = f.submit 'Update', 'ng-click' => "submit(null)", 'ng-disabled' => '!loaded()' - = f.submit 'Update and Close', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - %span{'ng-show' => 'loaded()'} - or - = link_to 'Cancel', main_app.admin_order_cycles_path - %span{'ng-hide' => 'loaded()'} Loading... - = render 'spree/admin/shared/status_message' + + %span{'ng-show' => 'loaded()'} + or + = link_to 'Cancel', main_app.admin_order_cycles_path + %span{'ng-hide' => 'loaded()'} Loading... + = render 'spree/admin/shared/status_message' - unless Rails.env.production? diff --git a/app/views/admin/order_cycles/_simple_form.html.haml b/app/views/admin/order_cycles/_simple_form.html.haml index 4dc64a012b..34ea062102 100644 --- a/app/views/admin/order_cycles/_simple_form.html.haml +++ b/app/views/admin/order_cycles/_simple_form.html.haml @@ -23,12 +23,9 @@ .actions - if @order_cycle.new_record? = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - - else - = f.submit 'Update', 'ng-click' => "submit(null)", 'ng-disabled' => '!loaded()' - = f.submit 'Update and Close', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - %span{'ng-show' => 'loaded()'} - or - = link_to 'Cancel', main_app.admin_order_cycles_path - %span{'ng-hide' => 'loaded()'} Loading... - = render 'spree/admin/shared/status_message' + %span{'ng-show' => 'loaded()'} + or + = link_to 'Cancel', main_app.admin_order_cycles_path + %span{'ng-hide' => 'loaded()'} Loading... + = render 'spree/admin/shared/status_message' diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 6ecb65d87e..4b2a397e49 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -28,7 +28,8 @@ - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f| - %save-bar{ save: "submit()", form: "order_cycle_form" } + + %save-bar{ buttons: "[{text: 'Update', param: null}, {text: 'Update and Close', param: '#{main_app.admin_order_cycles_path}'}]", save: "submit(param)", form: "order_cycle_form" } - if order_cycles_simple_form = render 'simple_form', f: f diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index c17e4c189a..70ae2c79bf 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,5 +1,5 @@ %form{ name: 'variant_overrides_form', ng: { show: "views.inventory.visible" } } - %save-bar{ save: "update()", form: "variant_overrides_form" } + %save-bar{ save: "update()", form: "variant_overrides_form", buttons: "[{text: 'Save Changes'}]" } %table.index.bulk#variant-overrides %col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } } %col.product{ width: "20%", ng: { show: 'columns.product.visible' } } diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 43983e87ea..b4d7de740d 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -10,7 +10,7 @@ = render :partial => 'spree/admin/shared/order_sub_menu' %div{ ng: { controller: 'LineItemsCtrl' } } - %save-bar{ save: "submit()", form: "bulk_order_form" } + %save-bar{ save: "submit()", form: "bulk_order_form", buttons: "[{text: 'Save Changes'}]" } .filters{ :class => "sixteen columns alpha" } .date_filter{ :class => "two columns alpha" } %label{ :for => 'start_date_filter' } From 70ce58f5e1d87dbc57ae831469c4ea29d257894f Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 27 Apr 2016 13:47:04 +1000 Subject: [PATCH 055/101] Tweak save_bar css --- app/assets/stylesheets/admin/components/save_bar.sass | 2 +- app/assets/stylesheets/admin/openfoodnetwork.css.scss | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass index c6b1236490..fcc60dbfc3 100644 --- a/app/assets/stylesheets/admin/components/save_bar.sass +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -3,7 +3,7 @@ bottom: 0px padding: 8px 10px font-weight: bold - background-color: #fff + background-color: #eff5fc color: #5498da h5 color: #5498da diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss index 17d43ff3a5..1fc95face8 100644 --- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss +++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss @@ -37,6 +37,7 @@ text-angular .ta-editor { input.red { background-color: #DA5354; + margin-right: 5px; } input.search { From 63b644551cef53c0d484369d04a029987abc8fc0 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 29 Apr 2016 10:43:28 +1000 Subject: [PATCH 056/101] Keep the action with save button --- .../javascripts/admin/order_cycles/controllers/edit.js.coffee | 2 +- .../javascripts/admin/utils/directives/save_bar.js.coffee | 1 - app/assets/javascripts/templates/admin/save_bar.html.haml | 4 ++-- app/views/admin/order_cycles/edit.html.haml | 2 +- app/views/admin/variant_overrides/_products.html.haml | 2 +- app/views/spree/admin/orders/bulk_management.html.haml | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee index a17f52e42c..068447d161 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee @@ -86,4 +86,4 @@ angular.module('admin.orderCycles') $scope.submit = (destination) -> StatusMessage.display 'progress', "Saving..." OrderCycle.update(destination) - this.order_cycle_form.$setPristine() + $scope.order_cycle_form.$setPristine() diff --git a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee index 9cd25d97cd..0999679394 100644 --- a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee +++ b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee @@ -1,7 +1,6 @@ angular.module("admin.utils").directive "saveBar", (StatusMessage) -> restrict: "E" scope: - save: "&" form: "=" buttons: "=" templateUrl: "admin/save_bar.html" diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index 83beac81a5..ddc535d001 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -1,8 +1,8 @@ -#save-bar.animate-show{ ng: { show: 'StatusMessage.active()' } } +#save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } } .twelve.columns.alpha %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } {{ StatusMessage.statusMessage.text || " " }} .four.columns.omega.text-right - %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "save({param: button.param})" } } + %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "button.action(button.param)" } } diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 4b2a397e49..4f3e5d8516 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -29,7 +29,7 @@ - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f| - %save-bar{ buttons: "[{text: 'Update', param: null}, {text: 'Update and Close', param: '#{main_app.admin_order_cycles_path}'}]", save: "submit(param)", form: "order_cycle_form" } + %save-bar{ buttons: "[{ text: 'Update', action: submit, param: null }, { text: 'Update and Close', action: submit, param: '#{main_app.admin_order_cycles_path}' }]", form: "order_cycle_form" } - if order_cycles_simple_form = render 'simple_form', f: f diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index 70ae2c79bf..34c8d02bb2 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,5 +1,5 @@ %form{ name: 'variant_overrides_form', ng: { show: "views.inventory.visible" } } - %save-bar{ save: "update()", form: "variant_overrides_form", buttons: "[{text: 'Save Changes'}]" } + %save-bar{ form: "variant_overrides_form", buttons: "[{ text: 'Save Changes', action: update }]" } %table.index.bulk#variant-overrides %col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } } %col.product{ width: "20%", ng: { show: 'columns.product.visible' } } diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index b4d7de740d..2bd58a4cc5 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -10,7 +10,7 @@ = render :partial => 'spree/admin/shared/order_sub_menu' %div{ ng: { controller: 'LineItemsCtrl' } } - %save-bar{ save: "submit()", form: "bulk_order_form", buttons: "[{text: 'Save Changes'}]" } + %save-bar{ form: "bulk_order_form", buttons: "[{ text: 'Save Changes', action: submit }]" } .filters{ :class => "sixteen columns alpha" } .date_filter{ :class => "two columns alpha" } %label{ :for => 'start_date_filter' } From 885d489bc3431f09ee8677ff381fd162ad62e4a9 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 29 Apr 2016 15:23:19 +1000 Subject: [PATCH 057/101] Fix failed test for the default form --- .../order_cycles/controllers/edit.js.coffee | 1 + spec/features/admin/order_cycles_spec.rb | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee index 068447d161..db63a8cbaa 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee @@ -63,6 +63,7 @@ angular.module('admin.orderCycles') $scope.removeExchange = ($event, exchange) -> $event.preventDefault() OrderCycle.removeExchange(exchange) + $scope.order_cycle_form.$dirty = true $scope.addCoordinatorFee = ($event) -> $event.preventDefault() diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index aafaa5ee77..4827f9b916 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -317,10 +317,10 @@ feature %q{ # And I add a supplier and some products select 'My supplier', from: 'new_supplier_id' click_button 'Add supplier' - page.all("table.exchanges tr.supplier td.products input").each { |e| e.click } + page.all("table.exchanges tr.supplier td.products input").each { |e| e.trigger('click') } page.should have_selector "#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true - page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).click # uncheck (with visible:true filter) + page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).trigger('click') # uncheck (with visible:true filter) check "order_cycle_incoming_exchange_2_variants_#{v1.id}" check "order_cycle_incoming_exchange_2_variants_#{v2.id}" @@ -343,7 +343,7 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_1_pickup_time', with: 'New time 1' fill_in 'order_cycle_outgoing_exchange_1_pickup_instructions', with: 'New instructions 1' - page.all("table.exchanges tr.distributor td.products input").each { |e| e.click } + page.all("table.exchanges tr.distributor td.products input").each { |e| e.trigger('click') } uncheck "order_cycle_outgoing_exchange_2_variants_#{v1.id}" check "order_cycle_outgoing_exchange_2_variants_#{v2.id}" @@ -359,7 +359,8 @@ feature %q{ select 'Distributor fee 2', from: 'order_cycle_outgoing_exchange_2_enterprise_fees_0_enterprise_fee_id' # And I click Update - click_button 'Update and Close' + expect(page).to have_selector "#save-bar" + find_button('Update and Close').trigger('click') # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' @@ -607,9 +608,9 @@ feature %q{ page.all('tr.supplier').count.should == 3 page.all('tr.distributor').count.should == 3 - # When I save, then those exchanges should remain - click_button 'Update' - page.should have_content "Your order cycle has been updated." + # # When I save, then those exchanges should remain + # click_button 'Update' + # page.should have_content "Your order cycle has been updated." oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] @@ -699,8 +700,8 @@ feature %q{ expect(page).to have_field "order_cycle_outgoing_exchange_0_variants_#{v2.id}", disabled: true # When I save, any exchanges that I can't manage remain - click_button 'Update' - page.should have_content "Your order cycle has been updated." + # click_button 'Update' + # page.should have_content "Your order cycle has been updated." oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] @@ -752,8 +753,8 @@ feature %q{ expect(page).to have_field "order_cycle_incoming_exchange_0_variants_#{v2.id}", disabled: true # When I save, any exchange that I can't manage remains - click_button 'Update' - page.should have_content "Your order cycle has been updated." + # click_button 'Update' + # page.should have_content "Your order cycle has been updated." oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] From 019e16c5ba87b403370e992e0208de559546ebd1 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Tue, 3 May 2016 22:58:09 +1000 Subject: [PATCH 058/101] Update AdminEditOrderCycleCtrl unit tests --- spec/javascripts/unit/order_cycle_spec.js.coffee | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 973b348383..2f1893bc43 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -9,7 +9,7 @@ describe 'OrderCycle controllers', -> EnterpriseFee = null beforeEach -> - scope = {} + scope = { order_cycle_form: { $dirty: false}} event = preventDefault: jasmine.createSpy('preventDefault') OrderCycle = @@ -169,7 +169,9 @@ describe 'OrderCycle controllers', -> EnterpriseFee = null beforeEach -> - scope = {} + scope = + order_cycle_form: jasmine.createSpyObj('order_cycle_form', ['$dirty', '$setPristine']) + $watch: jasmine.createSpy('$watch') event = preventDefault: jasmine.createSpy('preventDefault') location = @@ -292,6 +294,7 @@ describe 'OrderCycle controllers', -> scope.removeExchange(event, 'exchange') expect(event.preventDefault).toHaveBeenCalled() expect(OrderCycle.removeExchange).toHaveBeenCalledWith('exchange') + expect(scope.order_cycle_form.$dirty).toEqual true it 'Adds coordinator fees', -> scope.addCoordinatorFee(event) @@ -320,6 +323,7 @@ describe 'OrderCycle controllers', -> it 'Submits the order cycle via OrderCycle update', -> scope.submit('/admin/order_cycles') expect(OrderCycle.update).toHaveBeenCalledWith('/admin/order_cycles') + expect(scope.order_cycle_form.$setPristine.calls.length).toEqual 1 describe 'OrderCycle services', -> From 1833f0dd5d2283c6e0e52b82325ba9073aeec6b8 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 4 May 2016 11:22:25 +1000 Subject: [PATCH 059/101] Add save_bar to the order cycles simple editing form --- .../order_cycles/controllers/simple_edit.js.coffee | 4 ++++ spec/features/admin/order_cycles_spec.rb | 13 +++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee index bf00bb3df1..99c2e2649c 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee @@ -9,6 +9,9 @@ angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", $scope.order_cycle = OrderCycle.load $scope.orderCycleId(), (order_cycle) => $scope.init() + $scope.$watch 'order_cycle_form.$dirty', (newValue) -> + StatusMessage.display 'notice', 'You have unsaved changes' if newValue + $scope.loaded = -> Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded @@ -35,5 +38,6 @@ angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", OrderCycle.removeCoordinatorFee(index) $scope.submit = (destination) -> + StatusMessage.display 'progress', "Saving..." OrderCycle.mirrorIncomingToOutgoingProducts() OrderCycle.update(destination) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 4827f9b916..8b451fa7c0 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -889,10 +889,10 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'zzy' # And I make some product selections - uncheck "order_cycle_incoming_exchange_0_variants_#{v1.id}" - check "order_cycle_incoming_exchange_0_variants_#{v2.id}" - check "order_cycle_incoming_exchange_0_variants_#{v3.id}" - uncheck "order_cycle_incoming_exchange_0_variants_#{v3.id}" + find("#order_cycle_incoming_exchange_0_variants_#{v1.id}").trigger('click') + find("#order_cycle_incoming_exchange_0_variants_#{v2.id}").trigger('click') + find("#order_cycle_incoming_exchange_0_variants_#{v3.id}").trigger('click') + find("#order_cycle_incoming_exchange_0_variants_#{v3.id}").trigger('click') # And I select some fees and update click_link 'order_cycle_coordinator_fee_0_remove' @@ -901,9 +901,10 @@ feature %q{ select 'that fee', from: 'order_cycle_coordinator_fee_0_id' # When I update, or update and close, both work - click_button 'Update' + find_button('Update').trigger('click') page.should have_content 'Your order cycle has been updated.' - click_button 'Update and Close' + + find_button('Update and Close').trigger('click') # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' From 8b5e5105a8ce940e6fa6782852c2bb1776c96b29 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 4 May 2016 12:11:03 +1000 Subject: [PATCH 060/101] Fix failed tests --- spec/features/admin/order_cycles_spec.rb | 10 +++++----- .../order_cycles/controllers/simple_edit.js.coffee | 3 ++- spec/javascripts/unit/order_cycle_spec.js.coffee | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 8b451fa7c0..99069e9f7c 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -624,10 +624,10 @@ feature %q{ visit edit_admin_order_cycle_path(oc) # When I remove all the exchanges and save - page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").click - page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").click - page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").click - page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").click + page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").trigger('click') + page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").trigger('click') + page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").trigger('click') + page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").trigger('click') click_button 'Update' # Then the exchanges should be removed @@ -897,7 +897,7 @@ feature %q{ # And I select some fees and update click_link 'order_cycle_coordinator_fee_0_remove' page.should_not have_select 'order_cycle_coordinator_fee_0_id' - click_button 'Add coordinator fee' + find_button('Add coordinator fee').trigger('click') select 'that fee', from: 'order_cycle_coordinator_fee_0_id' # When I update, or update and close, both work diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee index dbad5a9f05..f5ccd25aac 100644 --- a/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee +++ b/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee @@ -10,7 +10,8 @@ describe "AdminSimpleEditOrderCycleCtrl", -> outgoing_exchange = {} beforeEach -> - scope = {} + scope = + $watch: jasmine.createSpy('$watch') location = absUrl: -> 'example.com/admin/order_cycles/27/edit' diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 2f1893bc43..b66acfcb1d 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -9,7 +9,7 @@ describe 'OrderCycle controllers', -> EnterpriseFee = null beforeEach -> - scope = { order_cycle_form: { $dirty: false}} + scope = {} event = preventDefault: jasmine.createSpy('preventDefault') OrderCycle = From 57ec7bb9a967df03b922e113241e088cfa187f26 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 4 May 2016 12:27:24 +1000 Subject: [PATCH 061/101] Remove unused test code --- .../stylesheets/admin/components/save_bar.sass | 2 +- spec/features/admin/order_cycles_spec.rb | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass index fcc60dbfc3..c6b1236490 100644 --- a/app/assets/stylesheets/admin/components/save_bar.sass +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -3,7 +3,7 @@ bottom: 0px padding: 8px 10px font-weight: bold - background-color: #eff5fc + background-color: #fff color: #5498da h5 color: #5498da diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 99069e9f7c..8caf156af4 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -608,10 +608,6 @@ feature %q{ page.all('tr.supplier').count.should == 3 page.all('tr.distributor').count.should == 3 - # # When I save, then those exchanges should remain - # click_button 'Update' - # page.should have_content "Your order cycle has been updated." - oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed @@ -699,10 +695,6 @@ feature %q{ # I should be able to see but not toggle v2, because I don't have permission expect(page).to have_field "order_cycle_outgoing_exchange_0_variants_#{v2.id}", disabled: true - # When I save, any exchanges that I can't manage remain - # click_button 'Update' - # page.should have_content "Your order cycle has been updated." - oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed @@ -752,10 +744,6 @@ feature %q{ # I should be able to see but not toggle v2, because I don't have permission expect(page).to have_field "order_cycle_incoming_exchange_0_variants_#{v2.id}", disabled: true - # When I save, any exchange that I can't manage remains - # click_button 'Update' - # page.should have_content "Your order cycle has been updated." - oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed From 3cac9c452f2cb78d8cb7ad32089a54759d2c6b10 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 4 May 2016 14:48:15 +1000 Subject: [PATCH 062/101] Tweaks --- app/views/admin/order_cycles/_form.html.haml | 8 +++----- app/views/admin/order_cycles/_simple_form.html.haml | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 13b317c314..ed2d3acbc2 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -53,11 +53,9 @@ - if @order_cycle.new_record? = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - %span{'ng-show' => 'loaded()'} - or - = link_to 'Cancel', main_app.admin_order_cycles_path - %span{'ng-hide' => 'loaded()'} Loading... - = render 'spree/admin/shared/status_message' + %span{'ng-show' => 'loaded()'} + = link_to 'Cancel', main_app.admin_order_cycles_path + %span{'ng-hide' => 'loaded()'} Loading... - unless Rails.env.production? diff --git a/app/views/admin/order_cycles/_simple_form.html.haml b/app/views/admin/order_cycles/_simple_form.html.haml index 34ea062102..9364d080ff 100644 --- a/app/views/admin/order_cycles/_simple_form.html.haml +++ b/app/views/admin/order_cycles/_simple_form.html.haml @@ -24,8 +24,6 @@ - if @order_cycle.new_record? = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - %span{'ng-show' => 'loaded()'} - or - = link_to 'Cancel', main_app.admin_order_cycles_path - %span{'ng-hide' => 'loaded()'} Loading... - = render 'spree/admin/shared/status_message' + %span{'ng-show' => 'loaded()'} + = link_to 'Cancel', main_app.admin_order_cycles_path + %span{'ng-hide' => 'loaded()'} Loading... From 7f1fc56f789cccb2fa49a6f42c0e27ea1fbd0856 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Wed, 4 May 2016 07:58:23 +0100 Subject: [PATCH 063/101] Change to use total rather than display_total (which returns a Spree::Money object) --- app/mailers/producer_mailer.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index d65d722894..52decf5d8a 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -46,12 +46,11 @@ class ProducerMailer < Spree::BaseMailer else lis[li.variant] = li end - lis end end def total_from_line_items(aggregated_line_items) - Spree::Money.new(aggregated_line_items.values.map(&:display_amount).reduce(:+)).to_s + Spree::Money.new(aggregated_line_items.values.map(&:total).reduce(:+)).to_s end end From 064e3c426ebf37028540d629dff22a059e2d33ae Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 6 May 2016 11:00:34 +1000 Subject: [PATCH 064/101] Make the save bar look better --- .../javascripts/templates/admin/save_bar.html.haml | 12 ++++++------ .../stylesheets/admin/components/save_bar.sass | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index ddc535d001..0a4a055c87 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -1,8 +1,8 @@ #save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } } - .twelve.columns.alpha - %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } - {{ StatusMessage.statusMessage.text || " " }} - .four.columns.omega.text-right - - %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "button.action(button.param)" } } + .container + .twelve.columns.alpha + %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } + {{ StatusMessage.statusMessage.text || " " }} + .four.columns.omega.text-right + %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "button.action(button.param)" } } diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass index c6b1236490..23585d05f5 100644 --- a/app/assets/stylesheets/admin/components/save_bar.sass +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -1,9 +1,11 @@ #save-bar position: fixed + width: 100% bottom: 0px - padding: 8px 10px + left: 0 + padding: 8px 8px font-weight: bold - background-color: #fff + background-color: #eff5fc color: #5498da h5 color: #5498da From 3e231da472d82a6353f211b5af6c38be881649d0 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 6 May 2016 11:13:15 +1000 Subject: [PATCH 065/101] Translate subjects of enterprise emails Minor text change Fix #906 Thanks to Nicolas Blanc: https://github.com/openfoodfoundation/openfoodnetwork/pull/937 --- app/mailers/enterprise_mailer.rb | 19 +++++++++++++------ config/locales/en.yml | 5 +++++ spec/mailers/enterprise_mailer_spec.rb | 4 ++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/app/mailers/enterprise_mailer.rb b/app/mailers/enterprise_mailer.rb index 248e31f109..dfd3f21636 100644 --- a/app/mailers/enterprise_mailer.rb +++ b/app/mailers/enterprise_mailer.rb @@ -4,19 +4,26 @@ class EnterpriseMailer < Spree::BaseMailer def welcome(enterprise) @enterprise = enterprise - mail(:to => enterprise.email, :from => from_address, - :subject => "#{enterprise.name} is now on #{Spree::Config[:site_name]}") + subject = t('enterprise_mailer.welcome.subject', + enterprise: @enterprise.name, + sitename: Spree::Config[:site_name]) + mail(:to => enterprise.email, + :from => from_address, + :subject => subject) end - def confirmation_instructions(record, token, opts={}) + def confirmation_instructions(record, token) @token = token find_enterprise(record) - mail(subject: "Please confirm your email for #{@enterprise.name}", - to: ( @enterprise.unconfirmed_email || @enterprise.email ), - from: from_address) + subject = t('enterprise_mailer.confirmation_instructions.subject', + enterprise: @enterprise.name) + mail(to: (@enterprise.unconfirmed_email || @enterprise.email), + from: from_address, + subject: subject) end private + def find_enterprise(enterprise) @enterprise = enterprise.is_a?(Enterprise) ? enterprise : Enterprise.find(enterprise) end diff --git a/config/locales/en.yml b/config/locales/en.yml index e9de6c9abd..b1595ba117 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -32,6 +32,11 @@ en: 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." + enterprise_mailer: + confirmation_instructions: + subject: "Please confirm the email address for %{enterprise}" + welcome: + subject: "%{enterprise} is now on %{sitename}" home: "OFN" title: Open Food Network welcome_to: 'Welcome to ' diff --git a/spec/mailers/enterprise_mailer_spec.rb b/spec/mailers/enterprise_mailer_spec.rb index eaa21b54ae..2399d4146a 100644 --- a/spec/mailers/enterprise_mailer_spec.rb +++ b/spec/mailers/enterprise_mailer_spec.rb @@ -12,7 +12,7 @@ describe EnterpriseMailer do EnterpriseMailer.confirmation_instructions(enterprise, 'token').deliver ActionMailer::Base.deliveries.count.should == 1 mail = ActionMailer::Base.deliveries.first - expect(mail.subject).to eq "Please confirm your email for #{enterprise.name}" + expect(mail.subject).to eq "Please confirm the email address for #{enterprise.name}" expect(mail.to).to include enterprise.email expect(mail.reply_to).to be_nil end @@ -28,7 +28,7 @@ describe EnterpriseMailer do EnterpriseMailer.confirmation_instructions(enterprise, 'token').deliver ActionMailer::Base.deliveries.count.should == 1 mail = ActionMailer::Base.deliveries.first - expect(mail.subject).to eq "Please confirm your email for #{enterprise.name}" + expect(mail.subject).to eq "Please confirm the email address for #{enterprise.name}" expect(mail.to).to include enterprise.unconfirmed_email end end From 08f0011244cf47a31a236b3bcf2d45d497c9528e Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 6 May 2016 12:43:50 +1000 Subject: [PATCH 066/101] Make the page long enough to avoid the save bar overlaying the form --- spec/features/admin/order_cycles_spec.rb | 38 ++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 8caf156af4..701780622f 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -267,6 +267,9 @@ feature %q{ scenario "updating an order cycle", js: true do + # Make the page long enough to avoid the save bar overlaying the form + page.driver.resize(1280, 3600) + # Given an order cycle with all the settings oc = create(:order_cycle) initial_variants = oc.variants.sort_by &:id @@ -317,10 +320,10 @@ feature %q{ # And I add a supplier and some products select 'My supplier', from: 'new_supplier_id' click_button 'Add supplier' - page.all("table.exchanges tr.supplier td.products input").each { |e| e.trigger('click') } + page.all("table.exchanges tr.supplier td.products input").each { |e| e.click } page.should have_selector "#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true - page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).trigger('click') # uncheck (with visible:true filter) + page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).click # uncheck (with visible:true filter) check "order_cycle_incoming_exchange_2_variants_#{v1.id}" check "order_cycle_incoming_exchange_2_variants_#{v2.id}" @@ -343,7 +346,7 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_1_pickup_time', with: 'New time 1' fill_in 'order_cycle_outgoing_exchange_1_pickup_instructions', with: 'New instructions 1' - page.all("table.exchanges tr.distributor td.products input").each { |e| e.trigger('click') } + page.all("table.exchanges tr.distributor td.products input").each { |e| e.click } uncheck "order_cycle_outgoing_exchange_2_variants_#{v1.id}" check "order_cycle_outgoing_exchange_2_variants_#{v2.id}" @@ -360,7 +363,8 @@ feature %q{ # And I click Update expect(page).to have_selector "#save-bar" - find_button('Update and Close').trigger('click') + save_screenshot('abc.png') + click_button 'Update and Close' # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' @@ -620,10 +624,11 @@ feature %q{ visit edit_admin_order_cycle_path(oc) # When I remove all the exchanges and save - page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").trigger('click') - page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").trigger('click') - page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").trigger('click') - page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").trigger('click') + page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").click + page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").click + page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").click + page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").click + click_button 'Update' # Then the exchanges should be removed @@ -857,6 +862,9 @@ feature %q{ end scenario "updating an order cycle" do + # Make the page long enough to avoid the save bar overlaying the form + page.driver.resize(1280, 3600) + # Given an order cycle with pickup time and instructions fee1 = create(:enterprise_fee, name: 'my fee', enterprise: enterprise) fee2 = create(:enterprise_fee, name: 'that fee', enterprise: enterprise) @@ -877,22 +885,22 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'zzy' # And I make some product selections - find("#order_cycle_incoming_exchange_0_variants_#{v1.id}").trigger('click') - find("#order_cycle_incoming_exchange_0_variants_#{v2.id}").trigger('click') - find("#order_cycle_incoming_exchange_0_variants_#{v3.id}").trigger('click') - find("#order_cycle_incoming_exchange_0_variants_#{v3.id}").trigger('click') + uncheck "order_cycle_incoming_exchange_0_variants_#{v1.id}" + check "order_cycle_incoming_exchange_0_variants_#{v2.id}" + check "order_cycle_incoming_exchange_0_variants_#{v3.id}" + uncheck "order_cycle_incoming_exchange_0_variants_#{v3.id}" # And I select some fees and update click_link 'order_cycle_coordinator_fee_0_remove' page.should_not have_select 'order_cycle_coordinator_fee_0_id' - find_button('Add coordinator fee').trigger('click') + click_button 'Add coordinator fee' select 'that fee', from: 'order_cycle_coordinator_fee_0_id' # When I update, or update and close, both work - find_button('Update').trigger('click') + click_button 'Update' page.should have_content 'Your order cycle has been updated.' - find_button('Update and Close').trigger('click') + click_button 'Update and Close' # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' From a37820a9636e6359a96828587f7d7438c7ebe7e6 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 6 May 2016 14:55:16 +1000 Subject: [PATCH 067/101] Resize window to fix failed test --- spec/features/admin/order_cycles_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 701780622f..cd5ef0a07e 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -619,6 +619,9 @@ feature %q{ end scenario "editing an order cycle" do + # Make the page long enough to avoid the save bar overlaying the form + page.driver.resize(1280, 3600) + oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) visit edit_admin_order_cycle_path(oc) From 6ecf896fa2b34509f5d915e4d8a92d957892ac90 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 10 May 2016 23:04:06 +0100 Subject: [PATCH 068/101] HTML order cycle report email, text customisable in translations --- app/assets/stylesheets/mail/email.css.sass | 3 + app/mailers/producer_mailer.rb | 5 ++ .../order_cycle_report.html.haml | 62 +++++++++++++++++++ .../order_cycle_report.text.haml | 4 +- config/locales/en.yml | 3 + spec/mailers/producer_mailer_spec.rb | 1 + 6 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 app/views/producer_mailer/order_cycle_report.html.haml diff --git a/app/assets/stylesheets/mail/email.css.sass b/app/assets/stylesheets/mail/email.css.sass index 4256357923..e9eabd4edb 100644 --- a/app/assets/stylesheets/mail/email.css.sass +++ b/app/assets/stylesheets/mail/email.css.sass @@ -74,6 +74,9 @@ table.order-summary padding-left: 5px padding-right: 5px +.text-right + text-align: right + .social .soc-btn padding: 3px 7px font-size: 12px diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index 52decf5d8a..9f776abc81 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -7,6 +7,7 @@ class ProducerMailer < Spree::BaseMailer @line_items = aggregated_line_items_from(@order_cycle, @producer) @receival_instructions = @order_cycle.receival_instructions_for @producer @total = total_from_line_items(@line_items) + @tax_total = tax_total_from_line_items(@line_items) subject = "[#{Spree::Config.site_name}] Order cycle report for #{producer.name}" @@ -53,4 +54,8 @@ class ProducerMailer < Spree::BaseMailer def total_from_line_items(aggregated_line_items) Spree::Money.new(aggregated_line_items.values.map(&:total).reduce(:+)).to_s end + + def tax_total_from_line_items(aggregated_line_items) + Spree::Money.new(aggregated_line_items.values.map(&:included_tax).reduce(:+)).to_s + end end diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml new file mode 100644 index 0000000000..66276745b5 --- /dev/null +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -0,0 +1,62 @@ +%p Dear #{@producer.name}, +%p + = t :producer_mail_text_before + - if @receival_instructions + %p + Stock pickup/delivery instructions: + = @receival_instructions +%p + Here is a summary of the orders for your products: + %table.order-summary + %thead + %tr + %th + = t :sku + %th + = t :supplier + %th + = t :product + %th.text-right + = t :quantity + %th.text-right + = t :price + %th.text-right + = t :subtotal + %th.text-right + = t :included_tax + %tbody + - @line_items.each_pair do |variant, line_item| + %tr + %td + #{variant.sku} + %td + #{raw(variant.product.supplier.name)} + %td + #{raw(variant.product_and_full_name)} + %td.text-right + #{line_item.quantity} + %td.text-right + #{line_item.single_money} + %td.text-right + #{line_item.display_total} + %td.text-right + #{line_item.display_included_tax} + %tr.total_row + %td + %td + %td + %td + %td + %td.text-right + #{@total} + %td.text-right + #{@tax_total} +%p + = t :producer_mail_text_after + %em + %p + #{@coordinator.name} + %p + #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} + #{@coordinator.phone} + #{@coordinator.email} diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml index ea035391b8..5c94107bd4 100644 --- a/app/views/producer_mailer/order_cycle_report.text.haml +++ b/app/views/producer_mailer/order_cycle_report.text.haml @@ -1,6 +1,6 @@ Dear #{@producer.name}, \ -We now have all the consumer orders for the next food drop. += t :producer_mail_text_before \ - if @receival_instructions Stock pickup/delivery instructions: @@ -18,7 +18,7 @@ Here is a summary of the orders for your products: \ Total: #{@total} \ -Thanks and best wishes, += t :producer_mail_text_after #{@coordinator.name} #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} #{@coordinator.phone} diff --git a/config/locales/en.yml b/config/locales/en.yml index 7b5f9b168f..4b53aa6e5a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -401,6 +401,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using If you are a producer or food enterprise, we are excited to have you as a part of the network." email_signup_help_html: "We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at" + producer_mail_text_before: "We now have all the consumer orders for the next food drop." + producer_mail_text_after: "Thanks and best wishes," + shopping_oc_closed: Orders are closed shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 4e3fd912d2..e1c91af699 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -63,6 +63,7 @@ describe ProducerMailer do end it "includes the total" do + puts mail.body.encoded mail.body.should include 'Total: $20.00' end From d5a7e907a3353d959c8dccd8177d08fe997b8f70 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 10 May 2016 23:11:14 +0100 Subject: [PATCH 069/101] Fix specs, need to add new for HTML --- .../producer_mailer/order_cycle_report.html.haml | 14 +++++++------- spec/mailers/producer_mailer_spec.rb | 7 +++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index 66276745b5..31cef99465 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -53,10 +53,10 @@ #{@tax_total} %p = t :producer_mail_text_after - %em - %p - #{@coordinator.name} - %p - #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} - #{@coordinator.phone} - #{@coordinator.email} + %em + %p + #{@coordinator.name} + %p + #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} + #{@coordinator.phone} + #{@coordinator.email} diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index e1c91af699..2ced290fed 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -44,7 +44,7 @@ describe ProducerMailer do end it "includes receival instructions" do - mail.body.should include 'Outside shed.' + mail.body.encoded.should include 'Outside shed.' end it "cc's the enterprise" do @@ -59,12 +59,11 @@ describe ProducerMailer do end it "does not include incomplete orders" do - mail.body.should_not include p3.name + mail.body.encoded.should_not include p3.name end it "includes the total" do - puts mail.body.encoded - mail.body.should include 'Total: $20.00' + mail.body.encoded.should include 'Total: $20.00' end it "sends no mail when the producer has no orders" do From 38efa218d08db5c3e696a1d1f656b97ed05fbf80 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 20 Apr 2016 15:31:11 +1000 Subject: [PATCH 070/101] Use save bar on order cycle form --- .../admin/order_cycles/controllers/edit.js.coffee | 5 +++++ app/assets/javascripts/templates/admin/save_bar.html.haml | 2 +- app/views/admin/order_cycles/edit.html.haml | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee index fd426eb455..a17f52e42c 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee @@ -12,6 +12,9 @@ angular.module('admin.orderCycles') $scope.StatusMessage = StatusMessage + $scope.$watch 'order_cycle_form.$dirty', (newValue) -> + StatusMessage.display 'notice', 'You have unsaved changes' if newValue + $scope.loaded = -> Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded @@ -81,4 +84,6 @@ angular.module('admin.orderCycles') OrderCycle.removeDistributionOfVariant(variant_id) $scope.submit = (destination) -> + StatusMessage.display 'progress', "Saving..." OrderCycle.update(destination) + this.order_cycle_form.$setPristine() diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index 452e81f6e3..ac4fa55de5 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -1,4 +1,4 @@ -#save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } } +#save-bar.animate-show{ ng: { show: 'StatusMessage.active()' } } .twelve.columns.alpha %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } {{ StatusMessage.statusMessage.text || " " }} diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index f3b0bdb721..6ecb65d87e 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -27,7 +27,9 @@ - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' -= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller} do |f| += form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f| + %save-bar{ save: "submit()", form: "order_cycle_form" } + - if order_cycles_simple_form = render 'simple_form', f: f - else From d10719330daaae1475c4e7e9d1d9d86edaa635a2 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 27 Apr 2016 12:15:20 +1000 Subject: [PATCH 071/101] Extend save_bar directive to support more buttons --- .../javascripts/admin/utils/directives/save_bar.js.coffee | 1 + app/assets/javascripts/templates/admin/save_bar.html.haml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee index 13e4f84bc6..9cd25d97cd 100644 --- a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee +++ b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee @@ -3,6 +3,7 @@ angular.module("admin.utils").directive "saveBar", (StatusMessage) -> scope: save: "&" form: "=" + buttons: "=" templateUrl: "admin/save_bar.html" link: (scope, element, attrs) -> scope.StatusMessage = StatusMessage diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index ac4fa55de5..83beac81a5 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -3,4 +3,6 @@ %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } {{ StatusMessage.statusMessage.text || " " }} .four.columns.omega.text-right - %input.red{type: "button", value: "Save Changes", ng: { disabled: '!form.$dirty', click: "save()" } } + + %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "save({param: button.param})" } } + From 2214c83ec7c644a172e25fab22d85a6294a82bce Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 27 Apr 2016 12:16:13 +1000 Subject: [PATCH 072/101] Use save_bar on order cycle update form --- app/views/admin/order_cycles/_form.html.haml | 14 ++++++-------- .../admin/order_cycles/_simple_form.html.haml | 13 +++++-------- app/views/admin/order_cycles/edit.html.haml | 3 ++- .../admin/variant_overrides/_products.html.haml | 2 +- .../spree/admin/orders/bulk_management.html.haml | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index c447d35d58..13b317c314 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -52,14 +52,12 @@ .actions - if @order_cycle.new_record? = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - - else - = f.submit 'Update', 'ng-click' => "submit(null)", 'ng-disabled' => '!loaded()' - = f.submit 'Update and Close', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - %span{'ng-show' => 'loaded()'} - or - = link_to 'Cancel', main_app.admin_order_cycles_path - %span{'ng-hide' => 'loaded()'} Loading... - = render 'spree/admin/shared/status_message' + + %span{'ng-show' => 'loaded()'} + or + = link_to 'Cancel', main_app.admin_order_cycles_path + %span{'ng-hide' => 'loaded()'} Loading... + = render 'spree/admin/shared/status_message' - unless Rails.env.production? diff --git a/app/views/admin/order_cycles/_simple_form.html.haml b/app/views/admin/order_cycles/_simple_form.html.haml index 4dc64a012b..34ea062102 100644 --- a/app/views/admin/order_cycles/_simple_form.html.haml +++ b/app/views/admin/order_cycles/_simple_form.html.haml @@ -23,12 +23,9 @@ .actions - if @order_cycle.new_record? = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - - else - = f.submit 'Update', 'ng-click' => "submit(null)", 'ng-disabled' => '!loaded()' - = f.submit 'Update and Close', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - %span{'ng-show' => 'loaded()'} - or - = link_to 'Cancel', main_app.admin_order_cycles_path - %span{'ng-hide' => 'loaded()'} Loading... - = render 'spree/admin/shared/status_message' + %span{'ng-show' => 'loaded()'} + or + = link_to 'Cancel', main_app.admin_order_cycles_path + %span{'ng-hide' => 'loaded()'} Loading... + = render 'spree/admin/shared/status_message' diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 6ecb65d87e..4b2a397e49 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -28,7 +28,8 @@ - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f| - %save-bar{ save: "submit()", form: "order_cycle_form" } + + %save-bar{ buttons: "[{text: 'Update', param: null}, {text: 'Update and Close', param: '#{main_app.admin_order_cycles_path}'}]", save: "submit(param)", form: "order_cycle_form" } - if order_cycles_simple_form = render 'simple_form', f: f diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index c17e4c189a..70ae2c79bf 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,5 +1,5 @@ %form{ name: 'variant_overrides_form', ng: { show: "views.inventory.visible" } } - %save-bar{ save: "update()", form: "variant_overrides_form" } + %save-bar{ save: "update()", form: "variant_overrides_form", buttons: "[{text: 'Save Changes'}]" } %table.index.bulk#variant-overrides %col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } } %col.product{ width: "20%", ng: { show: 'columns.product.visible' } } diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 43983e87ea..b4d7de740d 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -10,7 +10,7 @@ = render :partial => 'spree/admin/shared/order_sub_menu' %div{ ng: { controller: 'LineItemsCtrl' } } - %save-bar{ save: "submit()", form: "bulk_order_form" } + %save-bar{ save: "submit()", form: "bulk_order_form", buttons: "[{text: 'Save Changes'}]" } .filters{ :class => "sixteen columns alpha" } .date_filter{ :class => "two columns alpha" } %label{ :for => 'start_date_filter' } From 7fc37e4737339fca09eff6232facf39870c18632 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 27 Apr 2016 13:47:04 +1000 Subject: [PATCH 073/101] Tweak save_bar css --- app/assets/stylesheets/admin/components/save_bar.sass | 2 +- app/assets/stylesheets/admin/openfoodnetwork.css.scss | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass index c6b1236490..fcc60dbfc3 100644 --- a/app/assets/stylesheets/admin/components/save_bar.sass +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -3,7 +3,7 @@ bottom: 0px padding: 8px 10px font-weight: bold - background-color: #fff + background-color: #eff5fc color: #5498da h5 color: #5498da diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss index 17d43ff3a5..1fc95face8 100644 --- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss +++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss @@ -37,6 +37,7 @@ text-angular .ta-editor { input.red { background-color: #DA5354; + margin-right: 5px; } input.search { From 78b4a35d129843f4140608074cd5b62c18a36f48 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 29 Apr 2016 10:43:28 +1000 Subject: [PATCH 074/101] Keep the action with save button --- .../javascripts/admin/order_cycles/controllers/edit.js.coffee | 2 +- .../javascripts/admin/utils/directives/save_bar.js.coffee | 1 - app/assets/javascripts/templates/admin/save_bar.html.haml | 4 ++-- app/views/admin/order_cycles/edit.html.haml | 2 +- app/views/admin/variant_overrides/_products.html.haml | 2 +- app/views/spree/admin/orders/bulk_management.html.haml | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee index a17f52e42c..068447d161 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee @@ -86,4 +86,4 @@ angular.module('admin.orderCycles') $scope.submit = (destination) -> StatusMessage.display 'progress', "Saving..." OrderCycle.update(destination) - this.order_cycle_form.$setPristine() + $scope.order_cycle_form.$setPristine() diff --git a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee index 9cd25d97cd..0999679394 100644 --- a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee +++ b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee @@ -1,7 +1,6 @@ angular.module("admin.utils").directive "saveBar", (StatusMessage) -> restrict: "E" scope: - save: "&" form: "=" buttons: "=" templateUrl: "admin/save_bar.html" diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index 83beac81a5..ddc535d001 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -1,8 +1,8 @@ -#save-bar.animate-show{ ng: { show: 'StatusMessage.active()' } } +#save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } } .twelve.columns.alpha %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } {{ StatusMessage.statusMessage.text || " " }} .four.columns.omega.text-right - %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "save({param: button.param})" } } + %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "button.action(button.param)" } } diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 4b2a397e49..4f3e5d8516 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -29,7 +29,7 @@ - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f| - %save-bar{ buttons: "[{text: 'Update', param: null}, {text: 'Update and Close', param: '#{main_app.admin_order_cycles_path}'}]", save: "submit(param)", form: "order_cycle_form" } + %save-bar{ buttons: "[{ text: 'Update', action: submit, param: null }, { text: 'Update and Close', action: submit, param: '#{main_app.admin_order_cycles_path}' }]", form: "order_cycle_form" } - if order_cycles_simple_form = render 'simple_form', f: f diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index 70ae2c79bf..34c8d02bb2 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,5 +1,5 @@ %form{ name: 'variant_overrides_form', ng: { show: "views.inventory.visible" } } - %save-bar{ save: "update()", form: "variant_overrides_form", buttons: "[{text: 'Save Changes'}]" } + %save-bar{ form: "variant_overrides_form", buttons: "[{ text: 'Save Changes', action: update }]" } %table.index.bulk#variant-overrides %col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } } %col.product{ width: "20%", ng: { show: 'columns.product.visible' } } diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index b4d7de740d..2bd58a4cc5 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -10,7 +10,7 @@ = render :partial => 'spree/admin/shared/order_sub_menu' %div{ ng: { controller: 'LineItemsCtrl' } } - %save-bar{ save: "submit()", form: "bulk_order_form", buttons: "[{text: 'Save Changes'}]" } + %save-bar{ form: "bulk_order_form", buttons: "[{ text: 'Save Changes', action: submit }]" } .filters{ :class => "sixteen columns alpha" } .date_filter{ :class => "two columns alpha" } %label{ :for => 'start_date_filter' } From d59dea29ab8967fa8bb19eda77ba460e87d4860a Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 29 Apr 2016 15:23:19 +1000 Subject: [PATCH 075/101] Fix failed test for the default form --- .../order_cycles/controllers/edit.js.coffee | 1 + spec/features/admin/order_cycles_spec.rb | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee index 068447d161..db63a8cbaa 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee @@ -63,6 +63,7 @@ angular.module('admin.orderCycles') $scope.removeExchange = ($event, exchange) -> $event.preventDefault() OrderCycle.removeExchange(exchange) + $scope.order_cycle_form.$dirty = true $scope.addCoordinatorFee = ($event) -> $event.preventDefault() diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index aafaa5ee77..4827f9b916 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -317,10 +317,10 @@ feature %q{ # And I add a supplier and some products select 'My supplier', from: 'new_supplier_id' click_button 'Add supplier' - page.all("table.exchanges tr.supplier td.products input").each { |e| e.click } + page.all("table.exchanges tr.supplier td.products input").each { |e| e.trigger('click') } page.should have_selector "#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true - page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).click # uncheck (with visible:true filter) + page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).trigger('click') # uncheck (with visible:true filter) check "order_cycle_incoming_exchange_2_variants_#{v1.id}" check "order_cycle_incoming_exchange_2_variants_#{v2.id}" @@ -343,7 +343,7 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_1_pickup_time', with: 'New time 1' fill_in 'order_cycle_outgoing_exchange_1_pickup_instructions', with: 'New instructions 1' - page.all("table.exchanges tr.distributor td.products input").each { |e| e.click } + page.all("table.exchanges tr.distributor td.products input").each { |e| e.trigger('click') } uncheck "order_cycle_outgoing_exchange_2_variants_#{v1.id}" check "order_cycle_outgoing_exchange_2_variants_#{v2.id}" @@ -359,7 +359,8 @@ feature %q{ select 'Distributor fee 2', from: 'order_cycle_outgoing_exchange_2_enterprise_fees_0_enterprise_fee_id' # And I click Update - click_button 'Update and Close' + expect(page).to have_selector "#save-bar" + find_button('Update and Close').trigger('click') # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' @@ -607,9 +608,9 @@ feature %q{ page.all('tr.supplier').count.should == 3 page.all('tr.distributor').count.should == 3 - # When I save, then those exchanges should remain - click_button 'Update' - page.should have_content "Your order cycle has been updated." + # # When I save, then those exchanges should remain + # click_button 'Update' + # page.should have_content "Your order cycle has been updated." oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] @@ -699,8 +700,8 @@ feature %q{ expect(page).to have_field "order_cycle_outgoing_exchange_0_variants_#{v2.id}", disabled: true # When I save, any exchanges that I can't manage remain - click_button 'Update' - page.should have_content "Your order cycle has been updated." + # click_button 'Update' + # page.should have_content "Your order cycle has been updated." oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] @@ -752,8 +753,8 @@ feature %q{ expect(page).to have_field "order_cycle_incoming_exchange_0_variants_#{v2.id}", disabled: true # When I save, any exchange that I can't manage remains - click_button 'Update' - page.should have_content "Your order cycle has been updated." + # click_button 'Update' + # page.should have_content "Your order cycle has been updated." oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] From 26bb1a9beb353c36039b2c95fa52293b7860e67c Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Tue, 3 May 2016 22:58:09 +1000 Subject: [PATCH 076/101] Update AdminEditOrderCycleCtrl unit tests --- spec/javascripts/unit/order_cycle_spec.js.coffee | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 973b348383..2f1893bc43 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -9,7 +9,7 @@ describe 'OrderCycle controllers', -> EnterpriseFee = null beforeEach -> - scope = {} + scope = { order_cycle_form: { $dirty: false}} event = preventDefault: jasmine.createSpy('preventDefault') OrderCycle = @@ -169,7 +169,9 @@ describe 'OrderCycle controllers', -> EnterpriseFee = null beforeEach -> - scope = {} + scope = + order_cycle_form: jasmine.createSpyObj('order_cycle_form', ['$dirty', '$setPristine']) + $watch: jasmine.createSpy('$watch') event = preventDefault: jasmine.createSpy('preventDefault') location = @@ -292,6 +294,7 @@ describe 'OrderCycle controllers', -> scope.removeExchange(event, 'exchange') expect(event.preventDefault).toHaveBeenCalled() expect(OrderCycle.removeExchange).toHaveBeenCalledWith('exchange') + expect(scope.order_cycle_form.$dirty).toEqual true it 'Adds coordinator fees', -> scope.addCoordinatorFee(event) @@ -320,6 +323,7 @@ describe 'OrderCycle controllers', -> it 'Submits the order cycle via OrderCycle update', -> scope.submit('/admin/order_cycles') expect(OrderCycle.update).toHaveBeenCalledWith('/admin/order_cycles') + expect(scope.order_cycle_form.$setPristine.calls.length).toEqual 1 describe 'OrderCycle services', -> From 3760a34b2ba66f6e5bc615af8d41eb861219d97a Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 4 May 2016 11:22:25 +1000 Subject: [PATCH 077/101] Add save_bar to the order cycles simple editing form --- .../order_cycles/controllers/simple_edit.js.coffee | 4 ++++ spec/features/admin/order_cycles_spec.rb | 13 +++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee index bf00bb3df1..99c2e2649c 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee @@ -9,6 +9,9 @@ angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", $scope.order_cycle = OrderCycle.load $scope.orderCycleId(), (order_cycle) => $scope.init() + $scope.$watch 'order_cycle_form.$dirty', (newValue) -> + StatusMessage.display 'notice', 'You have unsaved changes' if newValue + $scope.loaded = -> Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded @@ -35,5 +38,6 @@ angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", OrderCycle.removeCoordinatorFee(index) $scope.submit = (destination) -> + StatusMessage.display 'progress', "Saving..." OrderCycle.mirrorIncomingToOutgoingProducts() OrderCycle.update(destination) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 4827f9b916..8b451fa7c0 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -889,10 +889,10 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'zzy' # And I make some product selections - uncheck "order_cycle_incoming_exchange_0_variants_#{v1.id}" - check "order_cycle_incoming_exchange_0_variants_#{v2.id}" - check "order_cycle_incoming_exchange_0_variants_#{v3.id}" - uncheck "order_cycle_incoming_exchange_0_variants_#{v3.id}" + find("#order_cycle_incoming_exchange_0_variants_#{v1.id}").trigger('click') + find("#order_cycle_incoming_exchange_0_variants_#{v2.id}").trigger('click') + find("#order_cycle_incoming_exchange_0_variants_#{v3.id}").trigger('click') + find("#order_cycle_incoming_exchange_0_variants_#{v3.id}").trigger('click') # And I select some fees and update click_link 'order_cycle_coordinator_fee_0_remove' @@ -901,9 +901,10 @@ feature %q{ select 'that fee', from: 'order_cycle_coordinator_fee_0_id' # When I update, or update and close, both work - click_button 'Update' + find_button('Update').trigger('click') page.should have_content 'Your order cycle has been updated.' - click_button 'Update and Close' + + find_button('Update and Close').trigger('click') # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' From e5d2e5010b72d93a9751091c017896141bfc2eac Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 4 May 2016 12:11:03 +1000 Subject: [PATCH 078/101] Fix failed tests --- spec/features/admin/order_cycles_spec.rb | 10 +++++----- .../order_cycles/controllers/simple_edit.js.coffee | 3 ++- spec/javascripts/unit/order_cycle_spec.js.coffee | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 8b451fa7c0..99069e9f7c 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -624,10 +624,10 @@ feature %q{ visit edit_admin_order_cycle_path(oc) # When I remove all the exchanges and save - page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").click - page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").click - page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").click - page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").click + page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").trigger('click') + page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").trigger('click') + page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").trigger('click') + page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").trigger('click') click_button 'Update' # Then the exchanges should be removed @@ -897,7 +897,7 @@ feature %q{ # And I select some fees and update click_link 'order_cycle_coordinator_fee_0_remove' page.should_not have_select 'order_cycle_coordinator_fee_0_id' - click_button 'Add coordinator fee' + find_button('Add coordinator fee').trigger('click') select 'that fee', from: 'order_cycle_coordinator_fee_0_id' # When I update, or update and close, both work diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee index dbad5a9f05..f5ccd25aac 100644 --- a/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee +++ b/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee @@ -10,7 +10,8 @@ describe "AdminSimpleEditOrderCycleCtrl", -> outgoing_exchange = {} beforeEach -> - scope = {} + scope = + $watch: jasmine.createSpy('$watch') location = absUrl: -> 'example.com/admin/order_cycles/27/edit' diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 2f1893bc43..b66acfcb1d 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -9,7 +9,7 @@ describe 'OrderCycle controllers', -> EnterpriseFee = null beforeEach -> - scope = { order_cycle_form: { $dirty: false}} + scope = {} event = preventDefault: jasmine.createSpy('preventDefault') OrderCycle = From ed1a06495bb38bfa8d6fe8c5786f43754548d754 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 4 May 2016 12:27:24 +1000 Subject: [PATCH 079/101] Remove unused test code --- .../stylesheets/admin/components/save_bar.sass | 2 +- spec/features/admin/order_cycles_spec.rb | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass index fcc60dbfc3..c6b1236490 100644 --- a/app/assets/stylesheets/admin/components/save_bar.sass +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -3,7 +3,7 @@ bottom: 0px padding: 8px 10px font-weight: bold - background-color: #eff5fc + background-color: #fff color: #5498da h5 color: #5498da diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 99069e9f7c..8caf156af4 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -608,10 +608,6 @@ feature %q{ page.all('tr.supplier').count.should == 3 page.all('tr.distributor').count.should == 3 - # # When I save, then those exchanges should remain - # click_button 'Update' - # page.should have_content "Your order cycle has been updated." - oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed @@ -699,10 +695,6 @@ feature %q{ # I should be able to see but not toggle v2, because I don't have permission expect(page).to have_field "order_cycle_outgoing_exchange_0_variants_#{v2.id}", disabled: true - # When I save, any exchanges that I can't manage remain - # click_button 'Update' - # page.should have_content "Your order cycle has been updated." - oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed @@ -752,10 +744,6 @@ feature %q{ # I should be able to see but not toggle v2, because I don't have permission expect(page).to have_field "order_cycle_incoming_exchange_0_variants_#{v2.id}", disabled: true - # When I save, any exchange that I can't manage remains - # click_button 'Update' - # page.should have_content "Your order cycle has been updated." - oc.reload oc.suppliers.should match_array [supplier_managed, supplier_permitted, supplier_unmanaged] oc.coordinator.should == distributor_managed From 7bf6881cb2f6707f3e1629457896918cfe4ac47f Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Wed, 4 May 2016 14:48:15 +1000 Subject: [PATCH 080/101] Tweaks --- app/views/admin/order_cycles/_form.html.haml | 8 +++----- app/views/admin/order_cycles/_simple_form.html.haml | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 13b317c314..ed2d3acbc2 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -53,11 +53,9 @@ - if @order_cycle.new_record? = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - %span{'ng-show' => 'loaded()'} - or - = link_to 'Cancel', main_app.admin_order_cycles_path - %span{'ng-hide' => 'loaded()'} Loading... - = render 'spree/admin/shared/status_message' + %span{'ng-show' => 'loaded()'} + = link_to 'Cancel', main_app.admin_order_cycles_path + %span{'ng-hide' => 'loaded()'} Loading... - unless Rails.env.production? diff --git a/app/views/admin/order_cycles/_simple_form.html.haml b/app/views/admin/order_cycles/_simple_form.html.haml index 34ea062102..9364d080ff 100644 --- a/app/views/admin/order_cycles/_simple_form.html.haml +++ b/app/views/admin/order_cycles/_simple_form.html.haml @@ -24,8 +24,6 @@ - if @order_cycle.new_record? = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' - %span{'ng-show' => 'loaded()'} - or - = link_to 'Cancel', main_app.admin_order_cycles_path - %span{'ng-hide' => 'loaded()'} Loading... - = render 'spree/admin/shared/status_message' + %span{'ng-show' => 'loaded()'} + = link_to 'Cancel', main_app.admin_order_cycles_path + %span{'ng-hide' => 'loaded()'} Loading... From 89ae77dfd9eee0ae0ff1c4932a90653215bff161 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 6 May 2016 11:00:34 +1000 Subject: [PATCH 081/101] Make the save bar look better --- .../javascripts/templates/admin/save_bar.html.haml | 12 ++++++------ .../stylesheets/admin/components/save_bar.sass | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index ddc535d001..0a4a055c87 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -1,8 +1,8 @@ #save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } } - .twelve.columns.alpha - %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } - {{ StatusMessage.statusMessage.text || " " }} - .four.columns.omega.text-right - - %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "button.action(button.param)" } } + .container + .twelve.columns.alpha + %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } + {{ StatusMessage.statusMessage.text || " " }} + .four.columns.omega.text-right + %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "button.action(button.param)" } } diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass index c6b1236490..23585d05f5 100644 --- a/app/assets/stylesheets/admin/components/save_bar.sass +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -1,9 +1,11 @@ #save-bar position: fixed + width: 100% bottom: 0px - padding: 8px 10px + left: 0 + padding: 8px 8px font-weight: bold - background-color: #fff + background-color: #eff5fc color: #5498da h5 color: #5498da From fed3ae9e858d4d60aa25251da43107e193fa1567 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 6 May 2016 12:43:50 +1000 Subject: [PATCH 082/101] Make the page long enough to avoid the save bar overlaying the form --- spec/features/admin/order_cycles_spec.rb | 38 ++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 8caf156af4..701780622f 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -267,6 +267,9 @@ feature %q{ scenario "updating an order cycle", js: true do + # Make the page long enough to avoid the save bar overlaying the form + page.driver.resize(1280, 3600) + # Given an order cycle with all the settings oc = create(:order_cycle) initial_variants = oc.variants.sort_by &:id @@ -317,10 +320,10 @@ feature %q{ # And I add a supplier and some products select 'My supplier', from: 'new_supplier_id' click_button 'Add supplier' - page.all("table.exchanges tr.supplier td.products input").each { |e| e.trigger('click') } + page.all("table.exchanges tr.supplier td.products input").each { |e| e.click } page.should have_selector "#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true - page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).trigger('click') # uncheck (with visible:true filter) + page.find("#order_cycle_incoming_exchange_1_variants_#{initial_variants.last.id}", visible: true).click # uncheck (with visible:true filter) check "order_cycle_incoming_exchange_2_variants_#{v1.id}" check "order_cycle_incoming_exchange_2_variants_#{v2.id}" @@ -343,7 +346,7 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_1_pickup_time', with: 'New time 1' fill_in 'order_cycle_outgoing_exchange_1_pickup_instructions', with: 'New instructions 1' - page.all("table.exchanges tr.distributor td.products input").each { |e| e.trigger('click') } + page.all("table.exchanges tr.distributor td.products input").each { |e| e.click } uncheck "order_cycle_outgoing_exchange_2_variants_#{v1.id}" check "order_cycle_outgoing_exchange_2_variants_#{v2.id}" @@ -360,7 +363,8 @@ feature %q{ # And I click Update expect(page).to have_selector "#save-bar" - find_button('Update and Close').trigger('click') + save_screenshot('abc.png') + click_button 'Update and Close' # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' @@ -620,10 +624,11 @@ feature %q{ visit edit_admin_order_cycle_path(oc) # When I remove all the exchanges and save - page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").trigger('click') - page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").trigger('click') - page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").trigger('click') - page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").trigger('click') + page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").click + page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").click + page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").click + page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").click + click_button 'Update' # Then the exchanges should be removed @@ -857,6 +862,9 @@ feature %q{ end scenario "updating an order cycle" do + # Make the page long enough to avoid the save bar overlaying the form + page.driver.resize(1280, 3600) + # Given an order cycle with pickup time and instructions fee1 = create(:enterprise_fee, name: 'my fee', enterprise: enterprise) fee2 = create(:enterprise_fee, name: 'that fee', enterprise: enterprise) @@ -877,22 +885,22 @@ feature %q{ fill_in 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'zzy' # And I make some product selections - find("#order_cycle_incoming_exchange_0_variants_#{v1.id}").trigger('click') - find("#order_cycle_incoming_exchange_0_variants_#{v2.id}").trigger('click') - find("#order_cycle_incoming_exchange_0_variants_#{v3.id}").trigger('click') - find("#order_cycle_incoming_exchange_0_variants_#{v3.id}").trigger('click') + uncheck "order_cycle_incoming_exchange_0_variants_#{v1.id}" + check "order_cycle_incoming_exchange_0_variants_#{v2.id}" + check "order_cycle_incoming_exchange_0_variants_#{v3.id}" + uncheck "order_cycle_incoming_exchange_0_variants_#{v3.id}" # And I select some fees and update click_link 'order_cycle_coordinator_fee_0_remove' page.should_not have_select 'order_cycle_coordinator_fee_0_id' - find_button('Add coordinator fee').trigger('click') + click_button 'Add coordinator fee' select 'that fee', from: 'order_cycle_coordinator_fee_0_id' # When I update, or update and close, both work - find_button('Update').trigger('click') + click_button 'Update' page.should have_content 'Your order cycle has been updated.' - find_button('Update and Close').trigger('click') + click_button 'Update and Close' # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' From 7040e4baae444804b1f9b8b1d4e534ae4bf39e0a Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 6 May 2016 14:55:16 +1000 Subject: [PATCH 083/101] Resize window to fix failed test --- spec/features/admin/order_cycles_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 701780622f..cd5ef0a07e 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -619,6 +619,9 @@ feature %q{ end scenario "editing an order cycle" do + # Make the page long enough to avoid the save bar overlaying the form + page.driver.resize(1280, 3600) + oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) visit edit_admin_order_cycle_path(oc) From f8ec0d316a943d019b8194ff7b43edb1d61710f4 Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Thu, 12 May 2016 10:52:48 +1000 Subject: [PATCH 084/101] Save bar can support cancel button --- .../admin/order_cycles/controllers/edit.js.coffee | 5 ++++- app/assets/javascripts/templates/admin/save_bar.html.haml | 6 +++--- app/assets/stylesheets/admin/components/save_bar.sass | 2 ++ app/assets/stylesheets/admin/openfoodnetwork.css.scss | 1 - app/views/admin/order_cycles/edit.html.haml | 2 +- app/views/admin/variant_overrides/_products.html.haml | 2 +- app/views/spree/admin/orders/bulk_management.html.haml | 2 +- 7 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee index db63a8cbaa..227c6a3045 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee @@ -1,5 +1,5 @@ angular.module('admin.orderCycles') - .controller 'AdminEditOrderCycleCtrl', ($scope, $filter, $location, OrderCycle, Enterprise, EnterpriseFee, StatusMessage) -> + .controller 'AdminEditOrderCycleCtrl', ($scope, $filter, $location, $window, OrderCycle, Enterprise, EnterpriseFee, StatusMessage) -> order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] $scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id) $scope.supplier_enterprises = Enterprise.producer_enterprises @@ -88,3 +88,6 @@ angular.module('admin.orderCycles') StatusMessage.display 'progress', "Saving..." OrderCycle.update(destination) $scope.order_cycle_form.$setPristine() + + $scope.cancel = (destination) -> + $window.location = destination diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index 0a4a055c87..0d4919dcde 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -1,8 +1,8 @@ #save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } } .container - .twelve.columns.alpha + .eight.columns.alpha %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } {{ StatusMessage.statusMessage.text || " " }} - .four.columns.omega.text-right - %input.red{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { disabled: '!form.$dirty', click: "button.action(button.param)" } } + .eight.columns.omega.text-right + %input{"ng-repeat" => "button in buttons", type: "button", value: "{{button.text}}", ng: { class: "button.class", click: "button.action(button.param)" } } diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass index 23585d05f5..87dcce82f9 100644 --- a/app/assets/stylesheets/admin/components/save_bar.sass +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -9,3 +9,5 @@ color: #5498da h5 color: #5498da + input + margin-right: 5px diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss index 1fc95face8..17d43ff3a5 100644 --- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss +++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss @@ -37,7 +37,6 @@ text-angular .ta-editor { input.red { background-color: #DA5354; - margin-right: 5px; } input.search { diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 4f3e5d8516..9f85261224 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -29,7 +29,7 @@ - ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f| - %save-bar{ buttons: "[{ text: 'Update', action: submit, param: null }, { text: 'Update and Close', action: submit, param: '#{main_app.admin_order_cycles_path}' }]", form: "order_cycle_form" } + %save-bar{ buttons: "[{ text: 'Update', action: submit, param: null, class: 'red' }, { text: 'Update and Close', action: submit, param: '#{main_app.admin_order_cycles_path}', class: 'red' }, { text: 'Cancel', action: cancel, param: '#{main_app.admin_order_cycles_path}', class: '' }]", form: "order_cycle_form" } - if order_cycles_simple_form = render 'simple_form', f: f diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index 34c8d02bb2..f255d86e0b 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,5 +1,5 @@ %form{ name: 'variant_overrides_form', ng: { show: "views.inventory.visible" } } - %save-bar{ form: "variant_overrides_form", buttons: "[{ text: 'Save Changes', action: update }]" } + %save-bar{ form: "variant_overrides_form", buttons: "[{ text: 'Save Changes', action: update, class: 'red' }]" } %table.index.bulk#variant-overrides %col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } } %col.product{ width: "20%", ng: { show: 'columns.product.visible' } } diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 2bd58a4cc5..d8228b5c83 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -10,7 +10,7 @@ = render :partial => 'spree/admin/shared/order_sub_menu' %div{ ng: { controller: 'LineItemsCtrl' } } - %save-bar{ form: "bulk_order_form", buttons: "[{ text: 'Save Changes', action: submit }]" } + %save-bar{ form: "bulk_order_form", buttons: "[{ text: 'Save Changes', action: submit, class: 'red' }]" } .filters{ :class => "sixteen columns alpha" } .date_filter{ :class => "two columns alpha" } %label{ :for => 'start_date_filter' } From c80255e9ab99141fbe24b422e28ffb843ea9eb00 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 1 May 2016 12:59:55 +0100 Subject: [PATCH 085/101] Add total to producer emails --- app/mailers/producer_mailer.rb | 5 +++++ app/views/producer_mailer/order_cycle_report.text.haml | 3 +++ spec/mailers/producer_mailer_spec.rb | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index b9fb52fb41..d65d722894 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -6,6 +6,7 @@ class ProducerMailer < Spree::BaseMailer @order_cycle = order_cycle @line_items = aggregated_line_items_from(@order_cycle, @producer) @receival_instructions = @order_cycle.receival_instructions_for @producer + @total = total_from_line_items(@line_items) subject = "[#{Spree::Config.site_name}] Order cycle report for #{producer.name}" @@ -49,4 +50,8 @@ class ProducerMailer < Spree::BaseMailer lis end end + + def total_from_line_items(aggregated_line_items) + Spree::Money.new(aggregated_line_items.values.map(&:display_amount).reduce(:+)).to_s + end end diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml index 93748395ce..ea035391b8 100644 --- a/app/views/producer_mailer/order_cycle_report.text.haml +++ b/app/views/producer_mailer/order_cycle_report.text.haml @@ -15,6 +15,9 @@ Here is a summary of the orders for your products: - @line_items.each_pair do |variant, line_item| #{variant.sku} - #{raw(variant.product.supplier.name)} - #{raw(variant.product_and_full_name)} (QTY: #{line_item.quantity}) @ #{line_item.single_money} = #{line_item.display_amount} \ +\ +Total: #{@total} +\ Thanks and best wishes, #{@coordinator.name} #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 0c7e82b89d..4e3fd912d2 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -62,6 +62,10 @@ describe ProducerMailer do mail.body.should_not include p3.name end + it "includes the total" do + mail.body.should include 'Total: $20.00' + end + it "sends no mail when the producer has no orders" do expect do ProducerMailer.order_cycle_report(s3, order_cycle).deliver From ef418c7f506dd38bfa371f188f1816c92907cd15 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Wed, 4 May 2016 07:58:23 +0100 Subject: [PATCH 086/101] Change to use total rather than display_total (which returns a Spree::Money object) --- app/mailers/producer_mailer.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index d65d722894..52decf5d8a 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -46,12 +46,11 @@ class ProducerMailer < Spree::BaseMailer else lis[li.variant] = li end - lis end end def total_from_line_items(aggregated_line_items) - Spree::Money.new(aggregated_line_items.values.map(&:display_amount).reduce(:+)).to_s + Spree::Money.new(aggregated_line_items.values.map(&:total).reduce(:+)).to_s end end From 559f7afc6047d74c915960d2b50885500093ad46 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 10 May 2016 23:04:06 +0100 Subject: [PATCH 087/101] HTML order cycle report email, text customisable in translations --- app/assets/stylesheets/mail/email.css.sass | 3 + app/mailers/producer_mailer.rb | 5 ++ .../order_cycle_report.html.haml | 62 +++++++++++++++++++ .../order_cycle_report.text.haml | 4 +- config/locales/en.yml | 3 + spec/mailers/producer_mailer_spec.rb | 1 + 6 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 app/views/producer_mailer/order_cycle_report.html.haml diff --git a/app/assets/stylesheets/mail/email.css.sass b/app/assets/stylesheets/mail/email.css.sass index 4256357923..e9eabd4edb 100644 --- a/app/assets/stylesheets/mail/email.css.sass +++ b/app/assets/stylesheets/mail/email.css.sass @@ -74,6 +74,9 @@ table.order-summary padding-left: 5px padding-right: 5px +.text-right + text-align: right + .social .soc-btn padding: 3px 7px font-size: 12px diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index 52decf5d8a..9f776abc81 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -7,6 +7,7 @@ class ProducerMailer < Spree::BaseMailer @line_items = aggregated_line_items_from(@order_cycle, @producer) @receival_instructions = @order_cycle.receival_instructions_for @producer @total = total_from_line_items(@line_items) + @tax_total = tax_total_from_line_items(@line_items) subject = "[#{Spree::Config.site_name}] Order cycle report for #{producer.name}" @@ -53,4 +54,8 @@ class ProducerMailer < Spree::BaseMailer def total_from_line_items(aggregated_line_items) Spree::Money.new(aggregated_line_items.values.map(&:total).reduce(:+)).to_s end + + def tax_total_from_line_items(aggregated_line_items) + Spree::Money.new(aggregated_line_items.values.map(&:included_tax).reduce(:+)).to_s + end end diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml new file mode 100644 index 0000000000..66276745b5 --- /dev/null +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -0,0 +1,62 @@ +%p Dear #{@producer.name}, +%p + = t :producer_mail_text_before + - if @receival_instructions + %p + Stock pickup/delivery instructions: + = @receival_instructions +%p + Here is a summary of the orders for your products: + %table.order-summary + %thead + %tr + %th + = t :sku + %th + = t :supplier + %th + = t :product + %th.text-right + = t :quantity + %th.text-right + = t :price + %th.text-right + = t :subtotal + %th.text-right + = t :included_tax + %tbody + - @line_items.each_pair do |variant, line_item| + %tr + %td + #{variant.sku} + %td + #{raw(variant.product.supplier.name)} + %td + #{raw(variant.product_and_full_name)} + %td.text-right + #{line_item.quantity} + %td.text-right + #{line_item.single_money} + %td.text-right + #{line_item.display_total} + %td.text-right + #{line_item.display_included_tax} + %tr.total_row + %td + %td + %td + %td + %td + %td.text-right + #{@total} + %td.text-right + #{@tax_total} +%p + = t :producer_mail_text_after + %em + %p + #{@coordinator.name} + %p + #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} + #{@coordinator.phone} + #{@coordinator.email} diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml index ea035391b8..5c94107bd4 100644 --- a/app/views/producer_mailer/order_cycle_report.text.haml +++ b/app/views/producer_mailer/order_cycle_report.text.haml @@ -1,6 +1,6 @@ Dear #{@producer.name}, \ -We now have all the consumer orders for the next food drop. += t :producer_mail_text_before \ - if @receival_instructions Stock pickup/delivery instructions: @@ -18,7 +18,7 @@ Here is a summary of the orders for your products: \ Total: #{@total} \ -Thanks and best wishes, += t :producer_mail_text_after #{@coordinator.name} #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} #{@coordinator.phone} diff --git a/config/locales/en.yml b/config/locales/en.yml index ac8d07bd7e..2186b01304 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -410,6 +410,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using If you are a producer or food enterprise, we are excited to have you as a part of the network." email_signup_help_html: "We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at" + producer_mail_text_before: "We now have all the consumer orders for the next food drop." + producer_mail_text_after: "Thanks and best wishes," + shopping_oc_closed: Orders are closed shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 4e3fd912d2..e1c91af699 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -63,6 +63,7 @@ describe ProducerMailer do end it "includes the total" do + puts mail.body.encoded mail.body.should include 'Total: $20.00' end From ab37cd25773cdbb42c741744a099aa3afd2795e3 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Tue, 10 May 2016 23:11:14 +0100 Subject: [PATCH 088/101] Fix specs, need to add new for HTML --- .../producer_mailer/order_cycle_report.html.haml | 14 +++++++------- spec/mailers/producer_mailer_spec.rb | 7 +++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index 66276745b5..31cef99465 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -53,10 +53,10 @@ #{@tax_total} %p = t :producer_mail_text_after - %em - %p - #{@coordinator.name} - %p - #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} - #{@coordinator.phone} - #{@coordinator.email} + %em + %p + #{@coordinator.name} + %p + #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} + #{@coordinator.phone} + #{@coordinator.email} diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index e1c91af699..2ced290fed 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -44,7 +44,7 @@ describe ProducerMailer do end it "includes receival instructions" do - mail.body.should include 'Outside shed.' + mail.body.encoded.should include 'Outside shed.' end it "cc's the enterprise" do @@ -59,12 +59,11 @@ describe ProducerMailer do end it "does not include incomplete orders" do - mail.body.should_not include p3.name + mail.body.encoded.should_not include p3.name end it "includes the total" do - puts mail.body.encoded - mail.body.should include 'Total: $20.00' + mail.body.encoded.should include 'Total: $20.00' end it "sends no mail when the producer has no orders" do From 9f56494c4c076a7f102b347a3074686aa3bfe507 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 13 May 2016 10:36:23 +1000 Subject: [PATCH 089/101] Simplify --- app/mailers/producer_mailer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index 9f776abc81..590193c4a5 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -52,10 +52,10 @@ class ProducerMailer < Spree::BaseMailer end def total_from_line_items(aggregated_line_items) - Spree::Money.new(aggregated_line_items.values.map(&:total).reduce(:+)).to_s + Spree::Money.new aggregated_line_items.values.map(&:total).sum end def tax_total_from_line_items(aggregated_line_items) - Spree::Money.new(aggregated_line_items.values.map(&:included_tax).reduce(:+)).to_s + Spree::Money.new aggregated_line_items.values.map(&:included_tax).sum end end From 027976626c2b5a6eaa6804e7c3e2900470a67ee5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 13 May 2016 11:19:44 +1000 Subject: [PATCH 090/101] Add spec for error summing Spree::Money --- spec/mailers/producer_mailer_spec.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 2ced290fed..1f7d5a6501 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -10,21 +10,23 @@ describe ProducerMailer do let(:p1) { create(:product, price: 12.34, supplier: s1) } let(:p2) { create(:product, price: 23.45, supplier: s2) } let(:p3) { create(:product, price: 34.56, supplier: s1) } + let(:p4) { create(:product, price: 45.67, supplier: s1) } let(:order_cycle) { create(:simple_order_cycle) } let!(:incoming_exchange) { order_cycle.exchanges.create! sender: s1, receiver: d1, incoming: true, receival_instructions: 'Outside shed.' } let!(:order) do order = create(:order, distributor: d1, order_cycle: order_cycle, state: 'complete') - order.line_items << create(:line_item, variant: p1.master) - order.line_items << create(:line_item, variant: p1.master) - order.line_items << create(:line_item, variant: p2.master) + order.line_items << create(:line_item, variant: p1.variants.first) + order.line_items << create(:line_item, variant: p1.variants.first) + order.line_items << create(:line_item, variant: p2.variants.first) + order.line_items << create(:line_item, variant: p4.variants.first) order.finalize! order.save order end let!(:order_incomplete) do order = create(:order, distributor: d1, order_cycle: order_cycle, state: 'payment') - order.line_items << create(:line_item, variant: p3.master) + order.line_items << create(:line_item, variant: p3.variants.first) order.save order end @@ -63,7 +65,7 @@ describe ProducerMailer do end it "includes the total" do - mail.body.encoded.should include 'Total: $20.00' + mail.body.encoded.should include 'Total: $30.00' end it "sends no mail when the producer has no orders" do From ba2d5548ff85d366276fca434c0b509a8fee76dd Mon Sep 17 00:00:00 2001 From: Bing Xie Date: Fri, 13 May 2016 15:44:34 +1000 Subject: [PATCH 091/101] Fix groups map view --- app/views/groups/show.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 74588176b9..b5222e5f86 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -40,6 +40,7 @@ .map-container %map{"ng-if" => "(active(\'\') && (mapShowed = true)) || mapShowed"} %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} + %map-search %markers{models: "mapMarkers", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} From 547fcf49e002adca8fa9bd87df7a63a7793ac489 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sat, 14 May 2016 11:00:13 +0100 Subject: [PATCH 092/101] Add line item included_tax_amount method to multiply by quantity --- app/mailers/producer_mailer.rb | 2 +- app/models/spree/line_item_decorator.rb | 13 +++++++++++-- spec/models/spree/line_item_spec.rb | 9 +++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index 590193c4a5..ae2f77384c 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -56,6 +56,6 @@ class ProducerMailer < Spree::BaseMailer end def tax_total_from_line_items(aggregated_line_items) - Spree::Money.new aggregated_line_items.values.map(&:included_tax).sum + Spree::Money.new aggregated_line_items.values.map(&:included_tax_amount).sum end end diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index 8a89d8fdef..f848aeede2 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -52,10 +52,15 @@ Spree::LineItem.class_eval do adjustments.included_tax.any? end + # Single def included_tax adjustments.included_tax.sum(&:included_tax) end + def included_tax_amount + included_tax * quantity + end + def price_with_adjustments # EnterpriseFee#create_locked_adjustment applies adjustments on line items to their parent order, # so line_item.adjustments returns an empty array @@ -77,8 +82,12 @@ Spree::LineItem.class_eval do Spree::Money.new(amount_with_adjustments, { :currency => currency }) end - def display_included_tax - Spree::Money.new(included_tax, { :currency => currency }) + def display_included_tax_amount + Spree::Money.new(included_tax_amount, { :currency => currency }) + end + + def display_amount_without_tax + Spree::Money.new(amount_with_adjustments - included_tax_amount, { currency: currency }) end def display_name diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index 111108f7b4..fb37fd4e36 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -124,6 +124,15 @@ module Spree expect(li_no_tax.included_tax).to eq 0.00 end end + + context "scaling included tax by quantity" do + it "multiplies included_tax" do + li_tax.quantity = 3 + li_tax.save + expect(li_tax.included_tax).to eq 10.00 + expect(li_tax.included_tax_amount).to eq 30.00 + end + end end describe "unit value/description" do From 08eaff1c24be8994afd3ab2578bb7a4bd7dafe4d Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 15 May 2016 23:10:38 +0100 Subject: [PATCH 093/101] Formatting changes --- .../order_cycle_report.html.haml | 20 ++++++++++++++----- config/locales/en.yml | 3 +++ spec/mailers/producer_mailer_spec.rb | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index 31cef99465..51472e2658 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -1,12 +1,15 @@ -%p Dear #{@producer.name}, +%p + = t :producer_mail_greeting + #{" " + @producer.name}, %p = t :producer_mail_text_before - if @receival_instructions %p - Stock pickup/delivery instructions: + %b + =t :producer_mail_delivery_instructions = @receival_instructions %p - Here is a summary of the orders for your products: + = t :producer_mail_order_text %table.order-summary %thead %tr @@ -40,7 +43,7 @@ %td.text-right #{line_item.display_total} %td.text-right - #{line_item.display_included_tax} + #{line_item.display_included_tax_amount} %tr.total_row %td %td @@ -57,6 +60,13 @@ %p #{@coordinator.name} %p - #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} + %br + #{@coordinator.address.address1} + %br + #{@coordinator.address.city} + %br + #{@coordinator.address.zipcode} + %p #{@coordinator.phone} + %p #{@coordinator.email} diff --git a/config/locales/en.yml b/config/locales/en.yml index 2186b01304..c16ed67872 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -410,7 +410,10 @@ See the %{link} to find out more about %{sitename}'s features and to start using If you are a producer or food enterprise, we are excited to have you as a part of the network." email_signup_help_html: "We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at" + producer_mail_greeting: "Dear" producer_mail_text_before: "We now have all the consumer orders for the next food drop." + producer_mail_order_text: "Here is a summary of the orders for your products:" + producer_mail_delivery_instructions: "Stock pickup/delivery instructions:" producer_mail_text_after: "Thanks and best wishes," shopping_oc_closed: Orders are closed diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 1f7d5a6501..2dec166aa3 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -65,6 +65,7 @@ describe ProducerMailer do end it "includes the total" do + puts mail.body.encoded mail.body.encoded.should include 'Total: $30.00' end From 38316bae3f0078689e48a93b528e356482dc5a93 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 15 May 2016 23:24:59 +0100 Subject: [PATCH 094/101] Add Stroudco wording to en-GB --- app/views/producer_mailer/order_cycle_report.html.haml | 2 ++ config/locales/en-GB.yml | 8 ++++++++ config/locales/en.yml | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index 51472e2658..49a34c0c98 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -56,6 +56,8 @@ #{@tax_total} %p = t :producer_mail_text_after +%p + #{t(:producer_mail_signoff)}, %em %p #{@coordinator.name} diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 2eae5eb8a1..5aa4dc3f0b 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -58,6 +58,14 @@ en-GB: sort_order_cycles_on_shopfront_by: "Sort Order Cycles On Shopfront By" + # To customise text in emails. + producer_mail_greeting: "Dear" + producer_mail_text_before: "We now have all the consumer orders for the next food drop." + producer_mail_order_text: "Here is a summary of the orders for your products:" + producer_mail_delivery_instructions: "Stock pickup/delivery instructions:" + producer_mail_text_after: "Please confirm that you have got this email. Please send me an invoice for this amount so that we can send you payment. If you need to phone me on the day, please use the number below." + producer_mail_signoff: "Thanks and best wishes" + admin: # General form elements diff --git a/config/locales/en.yml b/config/locales/en.yml index c16ed67872..aeb4a059ae 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -414,7 +414,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using producer_mail_text_before: "We now have all the consumer orders for the next food drop." producer_mail_order_text: "Here is a summary of the orders for your products:" producer_mail_delivery_instructions: "Stock pickup/delivery instructions:" - producer_mail_text_after: "Thanks and best wishes," + producer_mail_text_after: "" + producer_mail_signoff: "Thanks and best wishes" shopping_oc_closed: Orders are closed shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" From 93f0a7c58d45227e34e92f56bf5fd464f4fbf052 Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Sun, 15 May 2016 23:25:19 +0100 Subject: [PATCH 095/101] Remove debug statement --- spec/mailers/producer_mailer_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 2dec166aa3..1f7d5a6501 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -65,7 +65,6 @@ describe ProducerMailer do end it "includes the total" do - puts mail.body.encoded mail.body.encoded.should include 'Total: $30.00' end From 419402c5542b40a5ab335896ad69c80d4632912d Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Mon, 16 May 2016 21:30:56 +0100 Subject: [PATCH 096/101] Add some HTML email specs --- app/views/producer_mailer/order_cycle_report.html.haml | 2 +- spec/mailers/producer_mailer_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index 49a34c0c98..d74d13685f 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -44,7 +44,7 @@ #{line_item.display_total} %td.text-right #{line_item.display_included_tax_amount} - %tr.total_row + %tr.total-row %td %td %td diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 1f7d5a6501..b27245abe2 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -58,6 +58,9 @@ describe ProducerMailer do line.should include 'QTY: 2' line.should include '@ $10.00 = $20.00' end + Capybara.string(mail.html_part.body.encoded) + .find("table.order-summary tr", text: p1.name) + .should have_selector("td", text: "$20.00") end it "does not include incomplete orders" do @@ -66,6 +69,9 @@ describe ProducerMailer do it "includes the total" do mail.body.encoded.should include 'Total: $30.00' + Capybara.string(mail.html_part.body.encoded) + .find("tr.total-row") + .should have_selector("td", text: "$30.00") end it "sends no mail when the producer has no orders" do From c66ac0827ee1c17af3a640e1f9687153f0a565dc Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Mon, 16 May 2016 21:35:40 +0100 Subject: [PATCH 097/101] Add translations to plain text part --- app/views/producer_mailer/order_cycle_report.text.haml | 8 +++++--- spec/mailers/producer_mailer_spec.rb | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml index 5c94107bd4..31e2cf54d9 100644 --- a/app/views/producer_mailer/order_cycle_report.text.haml +++ b/app/views/producer_mailer/order_cycle_report.text.haml @@ -1,16 +1,16 @@ -Dear #{@producer.name}, +#{t :producer_mail_greeting} #{@producer.name}, \ = t :producer_mail_text_before \ - if @receival_instructions - Stock pickup/delivery instructions: + = t :producer_mail_delivery_instructions = @receival_instructions \ Orders summary ================ \ -Here is a summary of the orders for your products: += t :producer_mail_order_text \ - @line_items.each_pair do |variant, line_item| #{variant.sku} - #{raw(variant.product.supplier.name)} - #{raw(variant.product_and_full_name)} (QTY: #{line_item.quantity}) @ #{line_item.single_money} = #{line_item.display_amount} @@ -19,6 +19,8 @@ Here is a summary of the orders for your products: Total: #{@total} \ = t :producer_mail_text_after + +#{t :producer_mail_signoff}, #{@coordinator.name} #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} #{@coordinator.phone} diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index b27245abe2..01042b56f2 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -68,6 +68,7 @@ describe ProducerMailer do end it "includes the total" do + puts mail.text_part.body.encoded mail.body.encoded.should include 'Total: $30.00' Capybara.string(mail.html_part.body.encoded) .find("tr.total-row") From 858beb97c5ae44a0a46f59b30d7ec1e0e5f4425c Mon Sep 17 00:00:00 2001 From: Steve Pettitt Date: Wed, 18 May 2016 08:49:05 +0100 Subject: [PATCH 098/101] Temporarily comment out tax column --- app/views/producer_mailer/order_cycle_report.html.haml | 6 +++--- spec/mailers/producer_mailer_spec.rb | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index d74d13685f..5f16d8ef6b 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -25,7 +25,7 @@ = t :price %th.text-right = t :subtotal - %th.text-right + -#%th.text-right = t :included_tax %tbody - @line_items.each_pair do |variant, line_item| @@ -42,7 +42,7 @@ #{line_item.single_money} %td.text-right #{line_item.display_total} - %td.text-right + -#%td.text-right #{line_item.display_included_tax_amount} %tr.total-row %td @@ -52,7 +52,7 @@ %td %td.text-right #{@total} - %td.text-right + -#%td.text-right #{@tax_total} %p = t :producer_mail_text_after diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 01042b56f2..b27245abe2 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -68,7 +68,6 @@ describe ProducerMailer do end it "includes the total" do - puts mail.text_part.body.encoded mail.body.encoded.should include 'Total: $30.00' Capybara.string(mail.html_part.body.encoded) .find("tr.total-row") From d043de08cd94b90b7b63bdb5fcb2f42fcd0fe450 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 May 2016 09:42:19 +1000 Subject: [PATCH 099/101] Revert "Temporarily comment out tax column" This reverts commit 858beb97c5ae44a0a46f59b30d7ec1e0e5f4425c. --- app/views/producer_mailer/order_cycle_report.html.haml | 6 +++--- spec/mailers/producer_mailer_spec.rb | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index 5f16d8ef6b..d74d13685f 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -25,7 +25,7 @@ = t :price %th.text-right = t :subtotal - -#%th.text-right + %th.text-right = t :included_tax %tbody - @line_items.each_pair do |variant, line_item| @@ -42,7 +42,7 @@ #{line_item.single_money} %td.text-right #{line_item.display_total} - -#%td.text-right + %td.text-right #{line_item.display_included_tax_amount} %tr.total-row %td @@ -52,7 +52,7 @@ %td %td.text-right #{@total} - -#%td.text-right + %td.text-right #{@tax_total} %p = t :producer_mail_text_after diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index b27245abe2..01042b56f2 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -68,6 +68,7 @@ describe ProducerMailer do end it "includes the total" do + puts mail.text_part.body.encoded mail.body.encoded.should include 'Total: $30.00' Capybara.string(mail.html_part.body.encoded) .find("tr.total-row") From 6a3f6e7bfaf41e5448acd2133d3a9554726a44cd Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 May 2016 09:46:31 +1000 Subject: [PATCH 100/101] Revert "Add line item included_tax_amount method to multiply by quantity" This reverts commit 547fcf49e002adca8fa9bd87df7a63a7793ac489. --- app/mailers/producer_mailer.rb | 2 +- app/models/spree/line_item_decorator.rb | 13 ++----------- spec/models/spree/line_item_spec.rb | 9 --------- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index ae2f77384c..590193c4a5 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -56,6 +56,6 @@ class ProducerMailer < Spree::BaseMailer end def tax_total_from_line_items(aggregated_line_items) - Spree::Money.new aggregated_line_items.values.map(&:included_tax_amount).sum + Spree::Money.new aggregated_line_items.values.map(&:included_tax).sum end end diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index f848aeede2..8a89d8fdef 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -52,15 +52,10 @@ Spree::LineItem.class_eval do adjustments.included_tax.any? end - # Single def included_tax adjustments.included_tax.sum(&:included_tax) end - def included_tax_amount - included_tax * quantity - end - def price_with_adjustments # EnterpriseFee#create_locked_adjustment applies adjustments on line items to their parent order, # so line_item.adjustments returns an empty array @@ -82,12 +77,8 @@ Spree::LineItem.class_eval do Spree::Money.new(amount_with_adjustments, { :currency => currency }) end - def display_included_tax_amount - Spree::Money.new(included_tax_amount, { :currency => currency }) - end - - def display_amount_without_tax - Spree::Money.new(amount_with_adjustments - included_tax_amount, { currency: currency }) + def display_included_tax + Spree::Money.new(included_tax, { :currency => currency }) end def display_name diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index fb37fd4e36..111108f7b4 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -124,15 +124,6 @@ module Spree expect(li_no_tax.included_tax).to eq 0.00 end end - - context "scaling included tax by quantity" do - it "multiplies included_tax" do - li_tax.quantity = 3 - li_tax.save - expect(li_tax.included_tax).to eq 10.00 - expect(li_tax.included_tax_amount).to eq 30.00 - end - end end describe "unit value/description" do From 5d3adc0bdb33c7deea5f81667cc7a6e16c3e5487 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 May 2016 11:01:33 +1000 Subject: [PATCH 101/101] Fixing producer emails so that they calculate tax correctly Also listing items by full_name rather than by variant, to catch cases where line item weights/volumes have been adjusted --- app/mailers/producer_mailer.rb | 31 ++++--------- .../order_cycle_report.html.haml | 18 ++++---- .../order_cycle_report.text.haml | 4 +- spec/mailers/producer_mailer_spec.rb | 43 ++++++++++++------- 4 files changed, 46 insertions(+), 50 deletions(-) diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index 590193c4a5..30fad48f58 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -4,10 +4,11 @@ class ProducerMailer < Spree::BaseMailer @producer = producer @coordinator = order_cycle.coordinator @order_cycle = order_cycle - @line_items = aggregated_line_items_from(@order_cycle, @producer) + line_items = line_items_from(@order_cycle, @producer) + @grouped_line_items = line_items.group_by(&:product_and_full_name) @receival_instructions = @order_cycle.receival_instructions_for @producer - @total = total_from_line_items(@line_items) - @tax_total = tax_total_from_line_items(@line_items) + @total = total_from_line_items(line_items) + @tax_total = tax_total_from_line_items(line_items) subject = "[#{Spree::Config.site_name}] Order cycle report for #{producer.name}" @@ -27,10 +28,6 @@ class ProducerMailer < Spree::BaseMailer line_items_from(order_cycle, producer).any? end - def aggregated_line_items_from(order_cycle, producer) - aggregate_line_items line_items_from(order_cycle, producer) - end - def line_items_from(order_cycle, producer) Spree::LineItem. joins(:order => :order_cycle, :variant => :product). @@ -39,23 +36,11 @@ class ProducerMailer < Spree::BaseMailer merge(Spree::Order.complete) end - def aggregate_line_items(line_items) - # Arrange the items in a hash to group quantities - line_items.inject({}) do |lis, li| - if lis.key? li.variant - lis[li.variant].quantity += li.quantity - else - lis[li.variant] = li - end - lis - end + def total_from_line_items(line_items) + Spree::Money.new line_items.sum(&:total) end - def total_from_line_items(aggregated_line_items) - Spree::Money.new aggregated_line_items.values.map(&:total).sum - end - - def tax_total_from_line_items(aggregated_line_items) - Spree::Money.new aggregated_line_items.values.map(&:included_tax).sum + def tax_total_from_line_items(line_items) + Spree::Money.new line_items.sum(&:included_tax) end end diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index d74d13685f..b69dde8e66 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -28,22 +28,22 @@ %th.text-right = t :included_tax %tbody - - @line_items.each_pair do |variant, line_item| + - @grouped_line_items.each_pair do |product_and_full_name, line_items| %tr %td - #{variant.sku} + #{line_items.first.variant.sku} %td - #{raw(variant.product.supplier.name)} + #{raw(line_items.first.product.supplier.name)} %td - #{raw(variant.product_and_full_name)} + #{raw(product_and_full_name)} %td.text-right - #{line_item.quantity} + #{line_items.sum(&:quantity)} %td.text-right - #{line_item.single_money} + #{line_items.first.single_money} %td.text-right - #{line_item.display_total} - %td.text-right - #{line_item.display_included_tax_amount} + #{Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency) } + %td.tax.text-right + #{Spree::Money.new(line_items.sum(&:included_tax), currency: line_items.first.currency) } %tr.total-row %td %td diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml index 31e2cf54d9..b5b644bf01 100644 --- a/app/views/producer_mailer/order_cycle_report.text.haml +++ b/app/views/producer_mailer/order_cycle_report.text.haml @@ -12,8 +12,8 @@ Orders summary \ = t :producer_mail_order_text \ -- @line_items.each_pair do |variant, line_item| - #{variant.sku} - #{raw(variant.product.supplier.name)} - #{raw(variant.product_and_full_name)} (QTY: #{line_item.quantity}) @ #{line_item.single_money} = #{line_item.display_amount} +- @grouped_line_items.each_pair do |product_and_full_name, line_items| + #{line_items.first.variant.sku} - #{raw(line_items.first.product.supplier.name)} - #{raw(product_and_full_name)} (QTY: #{line_items.sum(&:quantity)}) @ #{line_items.first.single_money} = #{Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency)} \ \ Total: #{@total} diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 01042b56f2..b878c27895 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -2,12 +2,15 @@ require 'spec_helper' require 'yaml' describe ProducerMailer do + let!(:zone) { create(:zone_with_member) } + let!(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Spree::Calculator::DefaultTax.new, zone: zone, amount: 0.1) } + let!(:tax_category) { create(:tax_category, tax_rates: [tax_rate]) } let(:s1) { create(:supplier_enterprise) } let(:s2) { create(:supplier_enterprise) } let(:s3) { create(:supplier_enterprise) } - let(:d1) { create(:distributor_enterprise) } + let(:d1) { create(:distributor_enterprise, charges_sales_tax: true) } let(:d2) { create(:distributor_enterprise) } - let(:p1) { create(:product, price: 12.34, supplier: s1) } + let(:p1) { create(:product, price: 12.34, supplier: s1, tax_category: tax_category) } let(:p2) { create(:product, price: 23.45, supplier: s2) } let(:p3) { create(:product, price: 34.56, supplier: s1) } let(:p4) { create(:product, price: 45.67, supplier: s1) } @@ -16,10 +19,10 @@ describe ProducerMailer do let!(:order) do order = create(:order, distributor: d1, order_cycle: order_cycle, state: 'complete') - order.line_items << create(:line_item, variant: p1.variants.first) - order.line_items << create(:line_item, variant: p1.variants.first) - order.line_items << create(:line_item, variant: p2.variants.first) - order.line_items << create(:line_item, variant: p4.variants.first) + order.line_items << create(:line_item, quantity: 1, variant: p1.variants.first) + order.line_items << create(:line_item, quantity: 2, variant: p1.variants.first) + order.line_items << create(:line_item, quantity: 3, variant: p2.variants.first) + order.line_items << create(:line_item, quantity: 2, variant: p4.variants.first) order.finalize! order.save order @@ -55,12 +58,17 @@ describe ProducerMailer do it "contains an aggregated list of produce" do body_lines_including(mail, p1.name).each do |line| - line.should include 'QTY: 2' - line.should include '@ $10.00 = $20.00' + line.should include 'QTY: 3' + line.should include '@ $10.00 = $30.00' end - Capybara.string(mail.html_part.body.encoded) - .find("table.order-summary tr", text: p1.name) - .should have_selector("td", text: "$20.00") + body_as_html(mail).find("table.order-summary tr", text: p1.name) + .should have_selector("td", text: "$30.00") + end + + it "displays tax totals for each product" do + # Tax for p1 line items + body_as_html(mail).find("table.order-summary tr", text: p1.name) + .should have_selector("td", text: "$30.00") end it "does not include incomplete orders" do @@ -68,11 +76,10 @@ describe ProducerMailer do end it "includes the total" do - puts mail.text_part.body.encoded - mail.body.encoded.should include 'Total: $30.00' - Capybara.string(mail.html_part.body.encoded) - .find("tr.total-row") - .should have_selector("td", text: "$30.00") + # puts mail.text_part.body.encoded + mail.body.encoded.should include 'Total: $50.00' + body_as_html(mail).find("tr.total-row") + .should have_selector("td", text: "$50.00") end it "sends no mail when the producer has no orders" do @@ -87,4 +94,8 @@ describe ProducerMailer do def body_lines_including(mail, s) mail.body.to_s.lines.select { |line| line.include? s } end + + def body_as_html(mail) + Capybara.string(mail.html_part.body.encoded) + end end