diff --git a/app/assets/javascripts/admin/standing_orders/controllers/standing_order_controller.js.coffee b/app/assets/javascripts/admin/standing_orders/controllers/standing_order_controller.js.coffee index 1dd08c83ec..29bee6a6e0 100644 --- a/app/assets/javascripts/admin/standing_orders/controllers/standing_order_controller.js.coffee +++ b/app/assets/javascripts/admin/standing_orders/controllers/standing_order_controller.js.coffee @@ -11,7 +11,10 @@ angular.module("admin.standingOrders").controller "StandingOrderController", ($s $scope.save = -> $scope.standing_order_form.$setPristine() - StandingOrder.save() + if $scope.standingOrder.id? + StandingOrder.update() + else + StandingOrder.create() $scope.setView = (view) -> $scope.view = view @@ -22,7 +25,7 @@ angular.module("admin.standingOrders").controller "StandingOrderController", ($s $scope.estimatedSubtotal = -> $scope.standingOrder.standing_line_items.reduce (subtotal, item) -> - item.price_estimate * item.quantity + subtotal += item.price_estimate * item.quantity , 0 $scope.estimatedTotal = -> diff --git a/app/assets/javascripts/admin/standing_orders/services/standing_order.js.coffee b/app/assets/javascripts/admin/standing_orders/services/standing_order.js.coffee index bf0c692e38..381f73cc2d 100644 --- a/app/assets/javascripts/admin/standing_orders/services/standing_order.js.coffee +++ b/app/assets/javascripts/admin/standing_orders/services/standing_order.js.coffee @@ -16,7 +16,7 @@ angular.module("admin.standingOrders").factory "StandingOrder", ($injector, $htt , (response) => InfoDialog.open 'error', response.data.errors[0] - save: -> + create: -> StatusMessage.display 'progress', 'Saving...' delete @errors[k] for k, v of @errors @standingOrder.$save().then (response) => @@ -24,3 +24,12 @@ angular.module("admin.standingOrders").factory "StandingOrder", ($injector, $htt , (response) => StatusMessage.display 'failure', 'Oh no! I was unable to save your changes.' angular.extend(@errors, response.data.errors) + + update: -> + StatusMessage.display 'progress', 'Saving...' + delete @errors[k] for k, v of @errors + @standingOrder.$update().then (response) => + StatusMessage.display 'success', 'Saved' + , (response) => + StatusMessage.display 'failure', 'Oh no! I was unable to save your changes.' + angular.extend(@errors, response.data.errors) diff --git a/app/assets/javascripts/admin/standing_orders/services/standing_order_resource.js.coffee b/app/assets/javascripts/admin/standing_orders/services/standing_order_resource.js.coffee index 2d4167951d..9a689524c0 100644 --- a/app/assets/javascripts/admin/standing_orders/services/standing_order_resource.js.coffee +++ b/app/assets/javascripts/admin/standing_orders/services/standing_order_resource.js.coffee @@ -1,8 +1,10 @@ angular.module("admin.standingOrders").factory 'StandingOrderResource', ($resource) -> - $resource('/admin/standing_orders/:action.json', {}, { + $resource('/admin/standing_orders/:id/:action.json', {}, { 'index': method: 'GET' isArray: true - 'create': - method: 'POST' + 'update': + method: 'PUT' + params: + id: '@id' }) diff --git a/app/assets/stylesheets/admin/disabled.css.scss b/app/assets/stylesheets/admin/disabled.css.scss index defa38d33f..75f82686a8 100644 --- a/app/assets/stylesheets/admin/disabled.css.scss +++ b/app/assets/stylesheets/admin/disabled.css.scss @@ -12,4 +12,8 @@ input[type='button'], input[type='submit'] { .select2-container-disabled { pointer-events: none; + + .select2-choice > .select2-chosen { + color: #a1a1a1; + } } diff --git a/app/controllers/admin/standing_orders_controller.rb b/app/controllers/admin/standing_orders_controller.rb index 64ca07fd50..d2ed37031d 100644 --- a/app/controllers/admin/standing_orders_controller.rb +++ b/app/controllers/admin/standing_orders_controller.rb @@ -2,17 +2,19 @@ require 'open_food_network/permissions' module Admin class StandingOrdersController < ResourceController - before_filter :load_shop, only: [:new] before_filter :load_shops, only: [:index] - before_filter :wrap_nested_attrs, only: [:create] + before_filter :load_form_data, only: [:new, :edit] + before_filter :strip_banned_attrs, only: [:update] + before_filter :wrap_nested_attrs, only: [:create, :update] respond_to :json respond_override create: { json: { - success: lambda { - shop, next_oc = @standing_order.shop, @standing_order.schedule.current_or_next_order_cycle - fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, next_oc) if shop && next_oc - render_as_json @standing_order, fee_calculator: fee_calculator - }, + success: lambda { render_as_json @standing_order, fee_calculator: fee_calculator }, + failure: lambda { render json: { errors: json_errors }, status: :unprocessable_entity } + } } + + respond_override update: { json: { + success: lambda { render_as_json @standing_order, fee_calculator: fee_calculator }, failure: lambda { render json: { errors: json_errors }, status: :unprocessable_entity } } } @@ -24,13 +26,8 @@ module Admin end def new - @standing_order.shop = @shop @standing_order.bill_address = Spree::Address.new @standing_order.ship_address = Spree::Address.new - @customers = Customer.of(@shop) - @schedules = Schedule.with_coordinator(@shop) - @payment_methods = Spree::PaymentMethod.for_distributor(@shop) - @shipping_methods = Spree::ShippingMethod.for_distributor(@shop) end private @@ -49,14 +46,24 @@ module Admin end end - def load_shop - @shop = Enterprise.find(params[:shop_id]) - end - def load_shops @shops = Enterprise.managed_by(spree_current_user).is_distributor end + def load_form_data + @customers = Customer.of(@standing_order.shop) + @schedules = Schedule.with_coordinator(@standing_order.shop) + @payment_methods = Spree::PaymentMethod.for_distributor(@standing_order.shop) + @shipping_methods = Spree::ShippingMethod.for_distributor(@standing_order.shop) + @fee_calculator = fee_calculator + end + + def fee_calculator + shop, next_oc = @standing_order.shop, @standing_order.schedule.andand.current_or_next_order_cycle + return nil unless shop && next_oc + OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, next_oc) + end + def json_errors @object.errors.messages.inject({}) do |errors, (k,v)| errors[k] = v.map{ |msg| @object.errors.full_message(k,msg) } @@ -80,10 +87,15 @@ module Admin end end + def strip_banned_attrs + params[:standing_order].delete :schedule_id + params[:standing_order].delete :customer_id + end + # Overriding Spree method to load data from params here so that # we can authorise #create using an object with required attributes def build_resource - StandingOrder.new(shop_id: params[:standing_order].andand[:shop_id]) + StandingOrder.new(params[:standing_order]) end def ams_prefix_whitelist diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 99adfbb76c..0e93a4affc 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -120,7 +120,7 @@ module Admin end def admin_inject_json_ams(ngModule, name, data, serializer, opts = {}) - json = serializer.new(data, scope: spree_current_user).to_json + json = serializer.new(data, {scope: spree_current_user}.merge(opts)).to_json render partial: "admin/json/injection_ams", locals: {ngModule: ngModule, name: name, json: json} end diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index fad7540cb6..740bcc698f 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -253,7 +253,7 @@ class AbilityDecorator can [:create], Customer can [:admin, :index, :update, :destroy], Customer, enterprise_id: Enterprise.managed_by(user).pluck(:id) can [:admin, :new, :index], StandingOrder - can [:create], StandingOrder do |standing_order| + can [:create, :edit, :update], StandingOrder do |standing_order| user.enterprises.include?(standing_order.shop) end can [:admin, :build], StandingLineItem diff --git a/app/serializers/api/admin/index_standing_order_serializer.rb b/app/serializers/api/admin/index_standing_order_serializer.rb index 1fb1effa2d..a81532b140 100644 --- a/app/serializers/api/admin/index_standing_order_serializer.rb +++ b/app/serializers/api/admin/index_standing_order_serializer.rb @@ -1,5 +1,5 @@ class Api::Admin::IndexStandingOrderSerializer < ActiveModel::Serializer - attributes :id, :item_count, :begins_on, :ends_on + attributes :id, :item_count, :begins_on, :ends_on, :edit_path has_one :shop, serializer: Api::Admin::IdNameSerializer has_one :customer, serializer: Api::Admin::IdEmailSerializer # Remove IdEmailSerializer if no longer user here @@ -19,4 +19,8 @@ class Api::Admin::IndexStandingOrderSerializer < ActiveModel::Serializer def ends_on object.ends_at.andand.strftime('%a, %b %d, %Y') || I18n.t(:ongoing) end + + def edit_path + edit_admin_standing_order_path(object) + end end diff --git a/app/views/admin/standing_orders/_data.html.haml b/app/views/admin/standing_orders/_data.html.haml index 6507e94b65..40add13521 100644 --- a/app/views/admin/standing_orders/_data.html.haml +++ b/app/views/admin/standing_orders/_data.html.haml @@ -1,4 +1,4 @@ -= admin_inject_json_ams "admin.standingOrders", "standingOrder", @standing_order, Api::Admin::StandingOrderSerializer if @standing_order += admin_inject_json_ams "admin.standingOrders", "standingOrder", @standing_order, Api::Admin::StandingOrderSerializer, fee_calculator: @fee_calculator if @standing_order = admin_inject_json_ams_array "admin.standingOrders", "shops", @shops, Api::Admin::IdNameSerializer if @shops = admin_inject_json_ams_array "admin.standingOrders", "customers", @customers, Api::Admin::IdEmailSerializer if @customers = admin_inject_json_ams_array "admin.standingOrders", "schedules", @schedules, Api::Admin::IdNameSerializer if @schedules diff --git a/app/views/admin/standing_orders/_details.html.haml b/app/views/admin/standing_orders/_details.html.haml index 9a2caaa692..e0c4898d01 100644 --- a/app/views/admin/standing_orders/_details.html.haml +++ b/app/views/admin/standing_orders/_details.html.haml @@ -3,13 +3,13 @@ .row .seven.columns.alpha.field %label{ for: 'customer_id'}= t('admin.customer') - %input.ofn-select2.fullwidth#customer_id{ name: 'customer_id', type: 'number', data: 'customers', text: 'email', required: true, placeholder: t('admin.choose'), ng: { model: 'standingOrder.customer_id' } } + %input.ofn-select2.fullwidth#customer_id{ name: 'customer_id', type: 'number', data: 'customers', text: 'email', required: true, placeholder: t('admin.choose'), ng: { model: 'standingOrder.customer_id', disabled: 'standingOrder.id' } } .error{ ng: { show: 'submitted && standing_order_details_form.customer_id.$error.required' } }= t(:error_required) .error{ ng: { repeat: 'error in errors.customer', show: 'standing_order_details_form.customer_id.$pristine' } } {{ error }} .two.columns   .seven.columns.omega.field %label{ for: 'schedule_id'}= t('admin.schedule') - %input.ofn-select2.fullwidth#schedule_id{ name: 'schedule_id', type: 'number', data: 'schedules', required: true, placeholder: t('admin.choose'), ng: { model: 'standingOrder.schedule_id' } } + %input.ofn-select2.fullwidth#schedule_id{ name: 'schedule_id', type: 'number', data: 'schedules', required: true, placeholder: t('admin.choose'), ng: { model: 'standingOrder.schedule_id', disabled: 'standingOrder.id' } } .error{ ng: { show: 'submitted && standing_order_details_form.schedule_id.$error.required' } }= t(:error_required) .error{ ng: { repeat: 'error in errors.schedule', show: 'standing_order_details_form.schedule_id.$pristine'} } {{ error }} diff --git a/app/views/admin/standing_orders/_form.html.haml b/app/views/admin/standing_orders/_form.html.haml index 0de558a79b..76a4825db9 100644 --- a/app/views/admin/standing_orders/_form.html.haml +++ b/app/views/admin/standing_orders/_form.html.haml @@ -1,6 +1,6 @@ %form.margin-bottom-50{ name: 'standing_order_form', novalidate: true, ng: { submit: 'save()' } } %save-bar{ dirty: "standing_order_form.$dirty", persist: 'true', ng: { show: "view == 'review'" } } - %input.red{ type: "submit", value: t('admin.standing_orders.create') } + %input.red{ type: "submit", ng: { value: "standingOrder.id ? '#{t(:save_changes)}' : '#{t('admin.standing_orders.create')}'" } } .details{ ng: { show: "['details','review'].indexOf(view) >= 0" } } %ng-form{ name: 'standing_order_details_form', ng: { controller: 'DetailsController' } } diff --git a/app/views/admin/standing_orders/_products.html.haml b/app/views/admin/standing_orders/_products.html.haml index 1d54b24c0e..7be89b2acd 100644 --- a/app/views/admin/standing_orders/_products.html.haml +++ b/app/views/admin/standing_orders/_products.html.haml @@ -14,7 +14,7 @@ %span= t(:total) %th.orders-actions.actions %tbody - %tr.item{ ng: { repeat: 'item in standingOrder.standing_line_items', class: { even: 'even', odd: 'odd' } } } + %tr.item{ id: "sli_{{$index}}", ng: { repeat: 'item in standingOrder.standing_line_items', class: { even: 'even', odd: 'odd' } } } %td.description {{ item.description }} %td.price.align-center {{ item.price_estimate | currency }} %td.qty diff --git a/app/views/admin/standing_orders/_table.html.haml b/app/views/admin/standing_orders/_table.html.haml index 7bff479d09..99b18ec180 100644 --- a/app/views/admin/standing_orders/_table.html.haml +++ b/app/views/admin/standing_orders/_table.html.haml @@ -6,7 +6,7 @@ %col.ends_on{ width: "15%", 'ng-show' => 'columns.ends_on.visible' } %col.payment_method{ width: "20%", 'ng-show' => 'columns.payment_method.visible' } %col.shipping_method{ width: "20%", 'ng-show' => 'columns.shipping_method.visible' } - -# %col.actions + %col.actions{ width: "5%" } %thead %tr -# %th.bulk @@ -25,8 +25,8 @@ = t('admin.payment_method') %th.shipping_method{ ng: { show: 'columns.shipping_method.visible', } } = t('admin.shipping_method') - -# %th.actions - -#   + %th.actions +   %tr.standing_order{ :id => "so_{{standingOrder.id}}", ng: { repeat: "standingOrder in standingOrders | filter:query", class: { even: "'even'", odd: "'odd'" } } } %td.customer.text-center{ ng: { show: 'columns.customer.visible', bind: '::standingOrder.customer.email' } } %td.schedule.text-center{ ng: { show: 'columns.schedule.visible', bind: '::standingOrder.schedule.name' } } @@ -35,6 +35,6 @@ %td.ends_on.text-center{ ng: { show: 'columns.ends_on.visible', bind: '::standingOrder.ends_on' } } %td.payment_method{ ng: { show: 'columns.payment_method.visible', bind: '::standingOrder.payment_method.name' } } %td.shipping_method{ ng: { show: 'columns.shipping_method.visible', bind: '::standingOrder.shipping_method.name' } } - -# %td.actions - -# %a.edit-standing-order.icon-edit.no-text{ ng: { href: '{{standingOrder.edit_path}}'} } + %td.actions + %a.edit-standing-order.icon-edit.no-text{ ng: { href: '{{standingOrder.edit_path}}'} } -# %a.delete-standing-order.icon-trash.no-text{ ng: { href: '{{standingOrder.delete_path}}'}, data: { method: 'delete', confirm: "Are you sure?" } } diff --git a/app/views/admin/standing_orders/edit.html.haml b/app/views/admin/standing_orders/edit.html.haml new file mode 100644 index 0000000000..e5c440c655 --- /dev/null +++ b/app/views/admin/standing_orders/edit.html.haml @@ -0,0 +1,9 @@ +- content_for :page_title do + =t('admin.standing_orders.edit') + +-# - content_for :page_actions do +-# %li= button_link_to "Back to standing orders list", main_app.admin_standing_orders_path, icon: 'icon-arrow-left' + +%div{ ng: { app: 'admin.standingOrders', controller: 'StandingOrderController', cloak: true } } + = render 'data' + = render 'form' diff --git a/config/routes.rb b/config/routes.rb index 73184d903b..0056651de9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -177,7 +177,7 @@ Openfoodnetwork::Application.routes.draw do resources :schedules, only: [:index, :create, :update, :destroy], format: :json - resources :standing_orders, only: [:index, :new, :create] + resources :standing_orders, only: [:index, :new, :create, :edit, :update] resources :standing_line_items, only: [], format: :json do post :build, on: :collection diff --git a/spec/controllers/admin/standing_orders_controller_spec.rb b/spec/controllers/admin/standing_orders_controller_spec.rb index 6befaa8d6c..bb3043d7ec 100644 --- a/spec/controllers/admin/standing_orders_controller_spec.rb +++ b/spec/controllers/admin/standing_orders_controller_spec.rb @@ -87,8 +87,7 @@ describe Admin::StandingOrdersController, type: :controller do end it 'loads the preloads the necessary data' do - spree_get :new, shop_id: shop.id - expect(assigns(:shop)).to eq shop + spree_get :new, standing_order: { shop_id: shop.id } expect(assigns(:standing_order)).to be_a_new StandingOrder expect(assigns(:standing_order).shop).to eq shop expect(assigns(:customers)).to include customer1, customer2 @@ -214,4 +213,163 @@ describe Admin::StandingOrdersController, type: :controller do end end end + + describe 'edit' do + let!(:user) { create(:user) } + let!(:shop) { create(:distributor_enterprise, owner: user) } + let!(:customer1) { create(:customer, enterprise: shop) } + let!(:customer2) { create(:customer, enterprise: shop) } + let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop) } + let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } + let!(:payment_method) { create(:payment_method, distributors: [shop]) } + let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } + let!(:standing_order) { create(:standing_order, + shop: shop, + customer: customer1, + schedule: schedule, + payment_method: payment_method, + shipping_method: shipping_method + ) } + + before do + allow(controller).to receive(:spree_current_user) { user } + end + + it 'loads the preloads the necessary data' do + spree_get :edit, id: standing_order.id + expect(assigns(:standing_order)).to eq standing_order + expect(assigns(:customers)).to include customer1, customer2 + expect(assigns(:schedules)).to eq [schedule] + expect(assigns(:payment_methods)).to eq [payment_method] + expect(assigns(:shipping_methods)).to eq [shipping_method] + end + end + + describe 'update' do + let!(:user) { create(:user) } + let!(:shop) { create(:distributor_enterprise, owner: user) } + let!(:customer) { create(:customer, enterprise: shop) } + let!(:product1) { create(:product, supplier: shop) } + let!(:variant1) { create(:variant, product: product1, unit_value: '100', price: 12.00, option_values: []) } + let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } + let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now, orders_close_at: 7.days.from_now) } + let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [variant1], enterprise_fees: [enterprise_fee]) } + let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } + let!(:payment_method) { create(:payment_method, distributors: [shop]) } + let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } + let!(:standing_order) { create(:standing_order, + shop: shop, + customer: customer, + schedule: schedule, + payment_method: payment_method, + shipping_method: shipping_method, + standing_line_items: [create(:standing_line_item, variant: variant1, quantity: 2)] + ) } + let(:standing_line_item1) { standing_order.standing_line_items.first} + let(:params) { { format: :json, id: standing_order.id, standing_order: {} } } + + context 'as an non-manager of the standing order shop' do + before do + allow(controller).to receive(:spree_current_user) { create(:user, enterprises: [create(:enterprise)]) } + end + + it 'redirects to unauthorized' do + spree_post :update, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context 'as a manager of the standing_order shop' do + before do + allow(controller).to receive(:spree_current_user) { user } + end + + context 'when I submit params containing a new customer or schedule id' do + let!(:new_customer) { create(:customer, enterprise: shop) } + let!(:new_schedule) { create(:schedule, order_cycles: [order_cycle]) } + + before do + params[:standing_order].merge!({ schedule_id: new_schedule.id, customer_id: new_customer.id}) + end + + it 'does not alter customer_id or schedule_id' do + spree_post :update, params + standing_order.reload + expect(standing_order.customer).to eq customer + expect(standing_order.schedule).to eq schedule + end + end + + context 'when I submit params containing ids of inaccessible objects' do + # As 'user' I shouldnt be able to associate a standing_order with any of these. + let(:unmanaged_enterprise) { create(:enterprise) } + let(:unmanaged_payment_method) { create(:payment_method, distributors: [unmanaged_enterprise]) } + let(:unmanaged_shipping_method) { create(:shipping_method, distributors: [unmanaged_enterprise]) } + + before do + params[:standing_order].merge!({ + payment_method_id: unmanaged_payment_method.id, + shipping_method_id: unmanaged_shipping_method.id, + }) + end + + it 'returns errors' do + expect{ spree_post :update, params }.to_not change{StandingOrder.count} + json_response = JSON.parse(response.body) + expect(json_response['errors'].keys).to include 'payment_method', 'shipping_method' + standing_order.reload + expect(standing_order.payment_method).to eq payment_method + expect(standing_order.shipping_method).to eq shipping_method + end + end + + context 'when I submit valid params' do + let!(:new_payment_method) { create(:payment_method, distributors: [shop]) } + let!(:new_shipping_method) { create(:shipping_method, distributors: [shop]) } + + before do + params[:standing_order].merge!({payment_method_id: new_payment_method.id, shipping_method_id: new_shipping_method.id}) + end + + it 'updates the standing order' do + spree_post :update, params + standing_order.reload + expect(standing_order.schedule).to eq schedule + expect(standing_order.customer).to eq customer + expect(standing_order.payment_method).to eq new_payment_method + expect(standing_order.shipping_method).to eq new_shipping_method + end + + context 'with standing_line_items params' do + let!(:product2) { create(:product, supplier: shop) } + let!(:variant2) { create(:variant, product: product2, unit_value: '1000', price: 6.00, option_values: []) } + + before do + params[:standing_line_items] = [{id: standing_line_item1.id, quantity: 1, variant_id: variant1.id}, { quantity: 2, variant_id: variant2.id}] + end + + context 'where the specified variants are not available from the shop' do + it 'returns an error' do + expect{ spree_post :update, params }.to_not change{standing_order.standing_line_items.count} + json_response = JSON.parse(response.body) + expect(json_response['errors']['base']).to eq ["#{product2.name} - #{variant2.full_name} is not available from the selected schedule"] + end + end + + context 'where the specified variants are available from the shop' do + before { outgoing_exchange.update_attributes(variants: [variant1, variant2]) } + + it 'creates standing line items for the standing order' do + expect{ spree_post :update, params }.to change{standing_order.standing_line_items.count}.by(1) + standing_order.reload + expect(standing_order.standing_line_items.count).to be 2 + standing_line_item = standing_order.standing_line_items.last + expect(standing_line_item.quantity).to be 2 + expect(standing_line_item.variant).to eq variant2 + end + end + end + end + end + end end diff --git a/spec/factories.rb b/spec/factories.rb index 4a710bf01d..2cb8915dc0 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -149,6 +149,12 @@ FactoryGirl.define do begins_at { 1.month.ago } end + factory :standing_line_item, :class => StandingLineItem do + standing_order + variant + quantity 1 + end + factory :variant_override, :class => VariantOverride do price 77.77 count_on_hand 11111 diff --git a/spec/features/admin/standing_orders_spec.rb b/spec/features/admin/standing_orders_spec.rb index 6b8d48cd90..1149210809 100644 --- a/spec/features/admin/standing_orders_spec.rb +++ b/spec/features/admin/standing_orders_spec.rb @@ -72,7 +72,7 @@ feature 'Standing Orders' do let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } it "passes the smoke test" do - visit new_admin_standing_order_path(shop_id: shop.id) + visit new_admin_standing_order_path(standing_order: { shop_id: shop.id }) select2_select customer.email, from: 'customer_id' select2_select schedule.name, from: 'schedule_id' @@ -148,6 +148,62 @@ feature 'Standing Orders' do expect(standing_line_item.variant).to eq variant expect(standing_line_item.quantity).to eq 2 end + + context 'editing an existing standing order' do + let!(:customer) { create(:customer, enterprise: shop) } + let!(:product1) { create(:product, supplier: shop) } + let!(:product2) { create(:product, supplier: shop) } + let!(:variant1) { create(:variant, product: product1, unit_value: '100', price: 12.00, option_values: []) } + let!(:variant2) { create(:variant, product: product2, unit_value: '1000', price: 6.00, option_values: []) } + let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } + let!(:order_cycle) { create(:simple_order_cycle, coordinator: shop, orders_open_at: 2.days.from_now, orders_close_at: 7.days.from_now) } + let!(:outgoing_exchange) { order_cycle.exchanges.create(sender: shop, receiver: shop, variants: [variant1, variant2], enterprise_fees: [enterprise_fee]) } + let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } + let!(:payment_method) { create(:payment_method, distributors: [shop]) } + let!(:shipping_method) { create(:shipping_method, distributors: [shop]) } + let!(:standing_order) { create(:standing_order, + shop: shop, + customer: customer, + schedule: schedule, + payment_method: payment_method, + shipping_method: shipping_method, + standing_line_items: [create(:standing_line_item, variant: variant1, quantity: 2)] + ) } + + it "passes the smoke test" do + visit edit_admin_standing_order_path(standing_order) + + # Customer and Schedule cannot be edited + expect(page).to have_selector '#s2id_customer_id.select2-container-disabled' + expect(page).to have_selector '#s2id_schedule_id.select2-container-disabled' + + # Existing products should be visible + within "#sli_0" do + expect(page).to have_selector 'td.description', text: "#{product1.name} - #{variant1.full_name}" + expect(page).to have_selector 'td.price', text: "$13.75" + expect(page).to have_input 'quantity', with: "2" + expect(page).to have_selector 'td.total', text: "$27.50" + end + + # Add variant2 to the standing order + targetted_select2_search product2.name, from: '#add_variant_id', dropdown_css: '.select2-drop' + fill_in 'add_quantity', with: 1 + click_link 'Add' + within "#sli_1" do + expect(page).to have_selector 'td.description', text: "#{product2.name} - #{variant2.full_name}" + expect(page).to have_selector 'td.price', text: "$7.75" + expect(page).to have_input 'quantity', with: "1" + expect(page).to have_selector 'td.total', text: "$7.75" + end + + click_button 'Save Changes' + expect(page).to have_content 'Saved' + + # Total should be $35.25 + expect(page).to have_selector '#order_form_total', text: "$35.25" + expect(standing_order.reload.standing_line_items.length).to eq 2 + end + end end end end