spree-split-payments 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.gitignore +14 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE +26 -0
  6. data/README.md +96 -0
  7. data/Rakefile +15 -0
  8. data/app/assets/javascripts/admin/spree-split-payments.js +1 -0
  9. data/app/assets/javascripts/store/spree-split-payments.js +36 -0
  10. data/app/assets/stylesheets/admin/spree-split-payments.css +3 -0
  11. data/app/assets/stylesheets/store/spree-split-payments.css +3 -0
  12. data/app/controllers/spree/checkout_controller_decorator.rb +65 -0
  13. data/app/models/spree/order_decorator.rb +8 -0
  14. data/app/models/spree/payment_decorator.rb +13 -0
  15. data/app/models/spree/payment_method_decorator.rb +7 -0
  16. data/app/models/spree/user_decorator.rb +14 -0
  17. data/app/overrides/add_partial_payment_form_fields_admin.rb +25 -0
  18. data/app/overrides/add_split_payments_to_payment_form.rb +5 -0
  19. data/app/views/shared/_split_payments.html.erb +22 -0
  20. data/app/views/shared/_split_payments_data.html.erb +6 -0
  21. data/bin/rails +7 -0
  22. data/config/locales/en.yml +8 -0
  23. data/config/routes.rb +3 -0
  24. data/db/migrate/20140318063048_add_partial_payment_fields_to_spree_payment_method.rb +6 -0
  25. data/db/migrate/20140318063049_add_is_partial_to_spree_payment.rb +5 -0
  26. data/lib/generators/spree_split_payments/install/install_generator.rb +26 -0
  27. data/lib/spree-split-payments.rb +2 -0
  28. data/lib/spree_split_payments/engine.rb +23 -0
  29. data/lib/spree_split_payments/factories.rb +6 -0
  30. data/spec/controllers/spree/checkout_controller_spec.rb +122 -0
  31. data/spec/models/spree/order_decorator_spec.rb +25 -0
  32. data/spec/models/spree/payment_decorator_spec.rb +27 -0
  33. data/spec/models/spree/payment_method_decorator_spec.rb +45 -0
  34. data/spec/models/spree/user_decorator_spec.rb +19 -0
  35. data/spec/spec_helper.rb +62 -0
  36. data/spree-split-payments.gemspec +25 -0
  37. metadata +174 -0
@@ -0,0 +1,14 @@
1
+ \#*
2
+ *~
3
+ .#*
4
+ .DS_Store
5
+ .idea
6
+ .project
7
+ .sass-cache
8
+ coverage
9
+ Gemfile.lock
10
+ tmp
11
+ nbproject
12
+ pkg
13
+ *.swp
14
+ spec/dummy
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-2.1.0
4
+ script:
5
+ - bundle exec rake test_app
6
+ - bundle exec rspec .
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Provides basic authentication functionality for testing parts of your engine
4
+ gem 'spree_auth_devise', github: 'spree/spree_auth_devise', :branch => '2-1-stable'
5
+
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2014 Vinsol
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name Spree nor the names of its contributors may be used to
13
+ endorse or promote products derived from this software without specific
14
+ prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,96 @@
1
+ Spree Split Payments [![Code Climate](https://codeclimate.com/github/vinsol/spree-split-payments.png)](https://codeclimate.com/github/vinsol/spree-split-payments) [![Build Status](https://travis-ci.org/vinsol/spree-split-payments.png?branch=master)](https://travis-ci.org/vinsol/spree-split-payments)
2
+ =========================
3
+ This extension provides the feature for a spree store to allow user to club payment methods to pay for the order.
4
+
5
+ Easily configurable from the admin end where one can select which payment methods should be allowed for clubbing and their priorities which can be used while creating payments and displaying them to the user.
6
+
7
+ Installation
8
+ ------------
9
+
10
+ Add spree-split-payments to your `Gemfile`:
11
+
12
+ ```ruby
13
+ gem 'spree-split-payments'
14
+ ```
15
+
16
+ Bundle your dependencies and run the installation generator:
17
+
18
+ ```shell
19
+ bundle
20
+ bundle exec rails g spree-split-payments:install
21
+ ```
22
+
23
+ Integration
24
+ -----------
25
+ The extension needs a way to find out the maximum amount that can be made via a payment method. To do so it sends a message to the user object as:
26
+
27
+ ```ruby
28
+ #{payment_method.class.name.demodulize.underscore}_for_partial_payments
29
+
30
+ # for example : for Spree::PaymentMethod::LoyaltyPoints it calls for
31
+ # loyalty_points_for_partial_payments
32
+ # on current_user
33
+ ```
34
+
35
+ So you can either
36
+
37
+ 1) Alias an exising method like
38
+ ```ruby
39
+ # models/spree/user_decorator.rb
40
+ alias_method :loyalty_points_for_partial_payments, :loyalty_points_equivalent_currency
41
+
42
+ # where loyalty_points_equivalent_currency is the method provided by
43
+ # Spree::PaymentMethod::LoyaltyPoints extension.
44
+ ```
45
+
46
+ 2) Define a method under user class. For example for Spree::PaymentMethod::LoyaltyPoints)
47
+ ```ruby
48
+ #models/spree/user_decorator.rb
49
+ Spree::User.class_eval do
50
+ ...
51
+
52
+ def loyalty_points_for_partial_payments
53
+ # logic goes here
54
+ end
55
+
56
+ ...
57
+
58
+ end
59
+ ```
60
+
61
+ Testing
62
+ -------
63
+
64
+ Be sure to bundle your dependencies and then create a dummy test app for the specs to run against.
65
+
66
+ ```shell
67
+ bundle
68
+ bundle exec rake test_app
69
+ bundle exec rspec spec
70
+ ```
71
+
72
+ When testing your applications integration with this extension you may use it's factories.
73
+ Simply add this require statement to your `spec_helper`:
74
+
75
+ ```ruby
76
+ require 'spree-split-payments/factories'
77
+ ```
78
+
79
+ Contributing
80
+ ------------
81
+
82
+ 1. Fork the repo.
83
+ 2. Clone your repo.
84
+ 3. Run `bundle install`.
85
+ 4. Run `bundle exec rake test_app` to create the test application in `spec/test_app`.
86
+ 5. Make your changes.
87
+ 6. Ensure specs pass by running `bundle exec rspec spec`.
88
+ 7. Submit your pull request.
89
+
90
+
91
+ Credits
92
+ -------
93
+
94
+ [![vinsol.com: Ruby on Rails, iOS and Android developers](http://vinsol.com/vin_logo.png "Ruby on Rails, iOS and Android developers")](http://vinsol.com)
95
+
96
+ Copyright (c) 2014 [vinsol.com](http://vinsol.com "Ruby on Rails, iOS and Android developers"), released under the New MIT License
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'spree/testing_support/extension_rake'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default => [:spec]
10
+
11
+ desc 'Generates a dummy app for testing'
12
+ task :test_app do
13
+ ENV['LIB_NAME'] = 'spree-split-payments'
14
+ Rake::Task['extension:test_app'].invoke
15
+ end
@@ -0,0 +1 @@
1
+ //= require admin/spree_backend
@@ -0,0 +1,36 @@
1
+ Spree.fill_in_pm_amounts = function() {
2
+ // [TODO] Please extract the logic written below into a function with an appropriate name and call same function here.
3
+ amountDivs = $("#split-payments-data div");
4
+ if(amountDivs.length) {
5
+ for(i = 0; i < amountDivs.length; i++) {
6
+ $("label.pm-amount[data-pm-id='"+ amountDivs[i].getAttribute('data-pm-id') +"']").html(amountDivs[i].getAttribute('data-pm-amount'));
7
+ }
8
+ }
9
+ }
10
+
11
+ Spree.find_partial_payments_total = function(value) {
12
+ partial_payment_total = 0
13
+ selected_partial_methods = $("input[name='order[split_payments][][payment_method_id]']:checked");
14
+ if(selected_partial_methods.length) {
15
+ for(i = 0; i < selected_partial_methods.length; i++) {
16
+ partial_payment_total += parseInt($("#split-payments-data div[data-pm-id='"+ value +"']")[0].getAttribute('data-pm-amount'));
17
+ }
18
+ }
19
+ return partial_payment_total;
20
+ }
21
+
22
+ $(document).ready(function() {
23
+
24
+ Spree.fill_in_pm_amounts();
25
+
26
+ $("input[name='order[split_payments][][payment_method_id]']").click(function() {
27
+ // [TODO] Please extract the logic written below into a function with an appropriate name and call same function here.
28
+ // Also break into multiple functions if needed/possible.
29
+ partial_payment_total = Spree.find_partial_payments_total($(this).val());
30
+
31
+ if(partial_payment_total >= order_balance ) {
32
+ $(this).attr('checked', false);
33
+ alert('exceeding order total');
34
+ }
35
+ });
36
+ });
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require admin/spree_backend
3
+ */
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require store/spree_frontend
3
+ */
@@ -0,0 +1,65 @@
1
+ Spree::CheckoutController.class_eval do
2
+ before_action :insert_payments_using_split_payments,
3
+ only: :update, if: -> { process_split_payments? }
4
+
5
+ private
6
+ def insert_payments_using_split_payments
7
+ @order_balance_after_split_payment = @order.outstanding_balance
8
+
9
+ params['order']['split_payments'].each do |split_payment|
10
+ payment_method = Spree::PaymentMethod.supporting_partial_payments.where(id: split_payment['payment_method_id']).first
11
+ if payment_method
12
+ payment_amount = find_amount_for_partial_payment_for(payment_method);
13
+ insert_split_payment_for(split_payment['payment_method_id'], payment_amount) unless payment_amount.zero?
14
+ end
15
+ end
16
+ params['order'].delete('split_payments')
17
+ end
18
+
19
+ def insert_split_payment_for(payment_method_id, amount)
20
+ params[:order][:payments_attributes].insert(0,
21
+ { payment_method_id: payment_method_id,
22
+ is_partial: true,
23
+ amount: amount
24
+ }
25
+ )
26
+ @order_balance_after_split_payment -= amount
27
+ end
28
+
29
+ def find_amount_for_partial_payment_for(payment_method)
30
+ spree_current_user.maximum_partial_payment_for_payment_method(payment_method)
31
+ end
32
+
33
+ # over write this to use outstanding_amount instead of total amount
34
+ # for payment_attributes amount assignment
35
+ def object_params
36
+ # has_checkout_step? check is necessary due to issue described in #2910
37
+ if @order.has_checkout_step?('payment') && @order.payment?
38
+ if params[:payment_source].present?
39
+ source_params = params.delete(:payment_source)[params[:order][:payments_attributes].first[:payment_method_id].underscore]
40
+
41
+ if source_params
42
+ params[:order][:payments_attributes].first[:source_attributes] = source_params
43
+ end
44
+ end
45
+
46
+ if params[:order][:payments_attributes]
47
+ params[:order][:payments_attributes].last[:amount] = @order_balance_after_split_payment || @order.outstanding_balance
48
+ end
49
+ end
50
+
51
+ if params[:order]
52
+ params[:order].permit(permitted_checkout_attributes)
53
+ else
54
+ {}
55
+ end
56
+ end
57
+
58
+ def payment_step?
59
+ params[:state] == 'payment'
60
+ end
61
+
62
+ def process_split_payments?
63
+ payment_step? && params['order']['split_payments']
64
+ end
65
+ end
@@ -0,0 +1,8 @@
1
+ Spree::Order.class_eval do
2
+ Spree::Order.state_machine.before_transition :to => :complete, :do => :process_partial_payments
3
+
4
+ private
5
+ def process_partial_payments
6
+ self.payments.pending.partial.each { |payment| payment.complete }
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ Spree::Payment.class_eval do
2
+ after_create :mark_pending_if_partial, if: :is_partial?
3
+
4
+ def self.partial
5
+ where(is_partial: true)
6
+ end
7
+
8
+ private
9
+
10
+ def mark_pending_if_partial
11
+ self.pend
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ Spree::PaymentMethod.class_eval do
2
+ scope :supporting_partial_payments, -> { active.where(for_partial: true).order('partial_priority desc') }
3
+
4
+ def self.active
5
+ where(active: true)
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ Spree::User.class_eval do
2
+ # alias should be defined in the app integrating partial payments with
3
+ # any other payment method
4
+ # for example for Spree::LoyaltyPoints has a method for user to calculate
5
+ # amount quivalent to points as loyalty_points_equivalent_currency, so we do
6
+ # alias_method :loyalty_points_for_partial_payments,
7
+ # :loyalty_points_equivalent_currency
8
+
9
+ def maximum_partial_payment_for_payment_method(payment_method)
10
+ send(
11
+ "#{payment_method.class.name.demodulize.underscore}_for_partial_payments"
12
+ .to_sym)
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ # [TODO] 1..5 value in select code should not be hardcoded.
2
+ Deface::Override.new(
3
+ virtual_path: 'spree/admin/payment_methods/_form',
4
+ name: 'add partial payment form fields',
5
+ insert_after: "[data-hook='active']",
6
+ text: %q{
7
+ <div data-hook="for_partial" class="field">
8
+ <%= label_tag nil, Spree.t(:supports_partial_payment) %>
9
+ <ul>
10
+ <li>
11
+ <%= radio_button :payment_method, :for_partial, true %>
12
+ <%= label_tag nil, Spree.t(:say_yes) %>
13
+ </li>
14
+ <li>
15
+ <%= radio_button :payment_method, :for_partial, false %>
16
+ <%= label_tag nil, Spree.t(:say_no) %>
17
+ </li>
18
+ </ul>
19
+ </div>
20
+ <div data-hook="partial_priority" class="field">
21
+ <%= label_tag nil, Spree.t(:partial_priority) %>
22
+ <%= select_tag "payment_method[partial_priority]",
23
+ options_for_select(1..5, @object.partial_priority) %>
24
+ </div>
25
+ })
@@ -0,0 +1,5 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'spree/checkout/_payment',
3
+ name: 'add split payments to payment form',
4
+ insert_top: '#payment-method-fields',
5
+ partial: 'shared/split_payments')
@@ -0,0 +1,22 @@
1
+ <% partial_payment_methods = Spree::PaymentMethod.supporting_partial_payments %>
2
+ <% if partial_payment_methods.present? %>
3
+ <%= render :partial => '/shared/split_payments_data', :locals => { :payment_methods => partial_payment_methods } %>
4
+ Club my payment with
5
+ <table id="split_payment_options">
6
+ <% partial_payment_methods.each do |payment_method| %>
7
+ <tr>
8
+ <td>
9
+ <label>
10
+ <%= check_box_tag "order[split_payments][][payment_method_id]", payment_method.id %>
11
+ <%= Spree.t(payment_method.name) %>
12
+ </label>
13
+ (<label class="pm-amount" data-pm-id=<%= payment_method.id %>></label>)
14
+ </td>
15
+ </tr>
16
+ <% end %>
17
+ </table>
18
+ <% end %>
19
+ <!-- [TODO] No inline JS -->
20
+ <script>
21
+ var order_balance = <%= current_order.outstanding_balance %>
22
+ </script>
@@ -0,0 +1,6 @@
1
+ <div id="split-payments-data">
2
+ <% payment_methods.each do |payment_method| %>
3
+ <div data-pm-id=<%= payment_method.id %> data-pm-amount=<%= spree_current_user.maximum_partial_payment_for_payment_method(payment_method) %>>
4
+ </div>
5
+ <% end %>
6
+ </div>
@@ -0,0 +1,7 @@
1
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
2
+
3
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
4
+ ENGINE_PATH = File.expand_path('../../lib/spree-split-payments/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
@@ -0,0 +1,8 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
6
+ spree:
7
+ supports_partial_payment: "Use it for partial payments"
8
+ partial_priority: "Set Priority"
@@ -0,0 +1,3 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ # Add your extension routes here
3
+ end
@@ -0,0 +1,6 @@
1
+ class AddPartialPaymentFieldsToSpreePaymentMethod < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_payment_methods, :for_partial, :boolean, default: false
4
+ add_column :spree_payment_methods, :partial_priority, :integer, default: 0
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddIsPartialToSpreePayment < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_payments, :is_partial, :boolean, default: false
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ module SpreeSplitPayments
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ class_option :auto_run_migrations, type: :boolean, default: false
6
+
7
+ def add_javascripts
8
+ append_file 'app/assets/javascripts/store/all.js', "//= require store/spree-split-payments\n"
9
+ append_file 'app/assets/javascripts/admin/all.js', "//= require admin/spree-split-payments\n"
10
+ end
11
+
12
+ def add_stylesheets
13
+ inject_into_file 'app/assets/stylesheets/store/all.css', " *= require store/spree-split-payments\n", before: /\*\//, verbose: true
14
+ inject_into_file 'app/assets/stylesheets/admin/all.css', " *= require admin/spree-split-payments\n", before: /\*\//, verbose: true
15
+ end
16
+
17
+ def add_migrations
18
+ run 'bundle exec rake railties:install:migrations FROM=spree_split_payments'
19
+ end
20
+
21
+ def run_migrations
22
+ run 'bundle exec rake db:migrate'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,2 @@
1
+ require 'spree_core'
2
+ require 'spree_split_payments/engine'
@@ -0,0 +1,23 @@
1
+ module SpreeSplitPayments
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_split_payments'
6
+
7
+ config.autoload_paths += %W(#{config.root}/lib)
8
+
9
+ # use rspec for tests
10
+ config.generators do |g|
11
+ g.test_framework :rspec
12
+ end
13
+
14
+ def self.activate
15
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
16
+ Rails.configuration.cache_classes ? require(c) : load(c)
17
+ end
18
+ Spree::PermittedAttributes.payment_attributes << :is_partial
19
+ end
20
+
21
+ config.to_prepare &method(:activate).to_proc
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ # Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
3
+ #
4
+ # Example adding this to your spec_helper will load these Factories for use:
5
+ # require 'spree_split_payments/factories'
6
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::CheckoutController do
4
+ let(:user) { mock_model(Spree.user_class) }
5
+ let(:order) { Spree::Order.new }
6
+ let(:payment_method_partial_payment) { 10 }
7
+
8
+ before(:each) do
9
+ allow(controller).to receive(:spree_current_user).and_return(user)
10
+ allow(controller).to receive(:authorize!).and_return(true)
11
+ allow(order).to receive(:checkout_allowed?).and_return(true)
12
+ allow(controller).to receive(:load_order_with_lock).and_return(true)
13
+ user.stub(:last_incomplete_spree_order).and_return(order)
14
+ allow(controller).to receive(:ensure_order_not_completed).and_return(true)
15
+ allow(controller).to receive(:ensure_valid_state).and_return(true)
16
+ controller.instance_variable_set(:@order, order)
17
+ @payment_method = mock_model(Spree::PaymentMethod,
18
+ type: 'Spree::PaymentMethod::Check')
19
+ allow(Spree::PaymentMethod).to receive(:supporting_partial_payments).and_return(Spree::PaymentMethod)
20
+ # allow(Spree::PaymentMethod).to receive(:where).with({:deleted_at=>nil}).and_return(Spree::PaymentMethod)
21
+ allow(Spree::Payment.any_instance).to receive(:mark_pending_if_partial).and_return(true)
22
+ allow(order).to receive(:update_attributes).and_return(false)
23
+ allow(order).to receive(:outstanding_balance).and_return(100)
24
+ end
25
+
26
+ describe '#insert_payments_using_split_payments' do
27
+ def send_request(params = {})
28
+ put :update, params.merge!(use_route: 'spree')
29
+ end
30
+
31
+ context 'when state is not payment' do
32
+ it { controller.should_not_receive(:insert_payments_using_split_payments) }
33
+ after do
34
+ send_request(
35
+ order: {
36
+ payments_attributes: [{ payment_method_id: '1' }] },
37
+ state: 'delivery')
38
+ end
39
+ end
40
+
41
+ context 'when state is payment but without any split payment' do
42
+ it { controller.should_not_receive(:insert_payments_using_split_payments) }
43
+ after do
44
+ send_request(
45
+ order: {
46
+ payments_attributes: [{ payment_method_id: '1' }] },
47
+ state: 'payment')
48
+ end
49
+ end
50
+
51
+ context 'when state is payment and split payment attributes present' do
52
+ context 'payment method not found' do
53
+ it { expect(Spree::PaymentMethod).to receive(:supporting_partial_payments).and_return(Spree::PaymentMethod) }
54
+ it { Spree::PaymentMethod.should_receive(:where).with(id: '1').and_return([]) }
55
+ it { expect(controller).to_not receive(:find_amount_for_partial_payment_for) }
56
+ end
57
+
58
+ context 'payment method is found' do
59
+ before do
60
+ allow(Spree::PaymentMethod).to receive(:where).with(id: '1').and_return([@payment_method])
61
+ allow(user).to receive(:maximum_partial_payment_for_payment_method).with(@payment_method).and_return(payment_method_partial_payment)
62
+ # allow(controller).to receive(:insert_split_payment_for).with('1', payment_method_partial_payment).and_return(true)
63
+ end
64
+
65
+ it { expect(Spree::PaymentMethod).to receive(:where).with(id: '1').and_return([@payment_method]) }
66
+ it { expect(order).to receive(:outstanding_balance).and_return(order.total) }
67
+
68
+ context 'when payment amount is not zero' do
69
+ it { expect(user).to receive(:maximum_partial_payment_for_payment_method).with(@payment_method).and_return(payment_method_partial_payment) }
70
+ it { expect(controller).to receive(:insert_split_payment_for).with('1', payment_method_partial_payment).and_return(true) }
71
+ end
72
+
73
+ context 'when payment amount is zero' do
74
+ before { allow(user).to receive(:maximum_partial_payment_for_payment_method).with(@payment_method).and_return(0) }
75
+
76
+ it { expect(user).to receive(:maximum_partial_payment_for_payment_method).with(@payment_method).and_return(0) }
77
+ it { expect(controller).to_not receive(:insert_split_payment_for) }
78
+ end
79
+
80
+ context '#split payment inserted and amount is updated for last payment' do
81
+ before do
82
+ allow(order).to receive(:has_checkout_step?).and_return(true)
83
+ allow(order).to receive(:payment?).and_return(true)
84
+ end
85
+
86
+ it { expect(order).to receive(:update_attributes).with({
87
+ "payments_attributes" => [
88
+ {"amount"=>10, "payment_method_id"=>"1", "is_partial"=>true},
89
+ {"amount"=>90, "payment_method_id"=>"1"}
90
+ ]}).and_return(false) }
91
+ end
92
+ end
93
+
94
+ after do
95
+ send_request(
96
+ order: {
97
+ split_payments: [{ payment_method_id: '1' }],
98
+ payments_attributes: [{ payment_method_id: '1' }]},
99
+ state: 'payment')
100
+ end
101
+ end
102
+ end
103
+
104
+ describe '#object_params' do
105
+ def send_request(params = {})
106
+ put :update, params.merge!(use_route: 'spree')
107
+ end
108
+ before do
109
+ allow(order).to receive(:has_checkout_step?).and_return(true)
110
+ allow(order).to receive(:payment?).and_return(true)
111
+ end
112
+
113
+ it { expect(order).to receive(:outstanding_balance).and_return(order.total) }
114
+
115
+ after do
116
+ send_request(
117
+ order: {
118
+ payments_attributes: [{ payment_method_id: '1' }] },
119
+ state: 'payment')
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Order do
4
+ let(:order) { Spree::Order.create!(:email => 'test-account@myweb.com') }
5
+
6
+ describe 'process_partial_payments' do
7
+ before do
8
+ @payment = order.payments.build
9
+ @payments = [@payment]
10
+ allow(order).to receive(:payments).and_return(@payments)
11
+ allow(@payments).to receive(:pending).and_return(@payments)
12
+ allow(@payments).to receive(:partial).and_return(@payments)
13
+ end
14
+
15
+ it { expect(@payments).to receive(:pending).and_return(@payments) }
16
+ it { expect(@payments).to receive(:partial).and_return(@payments) }
17
+ it { expect(@payment).to receive(:complete).and_return(true) }
18
+
19
+ after { order.send(:process_partial_payments) }
20
+ end
21
+
22
+ context 'state machine' do
23
+ it { Spree::Order.state_machine.callbacks[:before].select { |callback| callback.instance_eval{@methods}.include?(:process_partial_payments) && callback.branch.state_requirements.any? {|req| req[:to].values.include?(:complete)} }.should_not be_blank }
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Spree::Payment' do
4
+ let(:order) { Spree::Order.create! }
5
+ context 'after create callback' do
6
+ context 'for partial payments' do
7
+ before do
8
+ @payment = order.payments.new(is_partial: true)
9
+ allow(@payment).to receive(:pend).and_return(true)
10
+ end
11
+
12
+ it 'calls for pend on save' do
13
+ expect(@payment).to receive(:pend).and_return(true)
14
+ @payment.save!
15
+ end
16
+ end
17
+
18
+ context 'for non-partial payments' do
19
+ before { @payment = order.payments.new }
20
+
21
+ it 'calls for pend on save' do
22
+ expect(@payment).to_not receive(:pend)
23
+ @payment.save!
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Spree::PaymentMethod' do
4
+ context 'scope' do
5
+ before :all do
6
+ @first_partial_method = Spree::PaymentMethod.create!(
7
+ name: 'test_method',
8
+ for_partial: true,
9
+ partial_priority: 1)
10
+ @second_partial_method = Spree::PaymentMethod.create!(
11
+ name: 'test_method_1',
12
+ for_partial: true,
13
+ partial_priority: 2)
14
+ @test_method = Spree::PaymentMethod.create!(
15
+ name: 'test_method_2')
16
+ @inactive_partial_method = Spree::PaymentMethod.create!(
17
+ name: 'test_method_1',
18
+ for_partial: true,
19
+ active: false)
20
+ @inactive_method = Spree::PaymentMethod.create!(
21
+ name: 'test_method_1',
22
+ active: false)
23
+ @partial_methods = Spree::PaymentMethod
24
+ .supporting_partial_payments
25
+ @active_methods = Spree::PaymentMethod.active
26
+ end
27
+
28
+ it 'supporting_partial_payments:is_partial? and active?' do
29
+ @partial_methods.to_a.should eq([@second_partial_method,
30
+ @first_partial_method])
31
+
32
+ @partial_methods.should_not include(@test_method,
33
+ @inactive_method,
34
+ @inactive_partial_method)
35
+ end
36
+
37
+ it 'active:active?' do
38
+ @active_methods.should include(@first_partial_method,
39
+ @second_partial_method,
40
+ @test_method)
41
+ @active_methods.should_not include(@inactive_method,
42
+ @inactive_partial_method)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Spree::User' do
4
+ let(:user) { Spree::User.new }
5
+ let(:order) { Spree::Order.new }
6
+ describe 'maximum_partial_payment_for_payment_method' do
7
+ before do
8
+ @method = Spree::PaymentMethod.create!(name: 'test_method',
9
+ type: 'Spree::Gateway::Bogus')
10
+ allow(user).to receive(:bogus_for_partial_payments).and_return(10)
11
+ end
12
+
13
+ it 'calls for method corresponding to payment method class name' do
14
+ expect(user).to receive(:bogus_for_partial_payments).and_return(10)
15
+ user.maximum_partial_payment_for_payment_method(@method)
16
+ .should eq(10)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require File.expand_path("../dummy/config/environment", __FILE__)
4
+ require 'rspec/rails'
5
+
6
+ require 'rubygems'
7
+ require 'bundler/setup'
8
+ require 'database_cleaner'
9
+
10
+ # require 'spree/testing_support/url_helpers'
11
+
12
+ # # Requires factories defined in lib/spree_spree-split-payments/factories.rb
13
+ # require 'spree_split_payments/factories'
14
+
15
+ RSpec.configure do |config|
16
+ # config.include FactoryGirl::Syntax::Methods
17
+
18
+ # == URL Helpers
19
+ #
20
+ # Allows access to Spree's routes in specs:
21
+ #
22
+ # visit spree.admin_path
23
+ # current_path.should eql(spree.products_path)
24
+ # config.include Spree::TestingSupport::UrlHelpers
25
+
26
+ # == Mock Framework
27
+ #
28
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
29
+ #
30
+ # config.mock_with :mocha
31
+ # config.mock_with :flexmock
32
+ # config.mock_with :rr
33
+ config.mock_with :rspec
34
+ config.color = true
35
+
36
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
37
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
38
+
39
+ # Capybara javascript drivers require transactional fixtures set to false, and we use DatabaseCleaner
40
+ # to cleanup after each test instead. Without transactional fixtures set to false the records created
41
+ # to setup a test will be unavailable to the browser, which runs under a seperate server instance.
42
+ config.use_transactional_fixtures = false
43
+
44
+ # Ensure Suite is set to use transactions for speed.
45
+ config.before :suite do
46
+ DatabaseCleaner.strategy = :transaction
47
+ DatabaseCleaner.clean_with :truncation
48
+ end
49
+
50
+ # Before each spec check if it is a Javascript test and switch between using database transactions or not where necessary.
51
+ config.before :each do
52
+ DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
53
+ DatabaseCleaner.start
54
+ end
55
+
56
+ # After each spec clean the database.
57
+ config.after :each do
58
+ DatabaseCleaner.clean
59
+ end
60
+
61
+ config.fail_fast = ENV['FAIL_FAST'] || false
62
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+ Gem::Specification.new do |s|
3
+ s.platform = Gem::Platform::RUBY
4
+ s.name = 'spree-split-payments'
5
+ s.version = '1.0.0'
6
+ s.required_ruby_version = '>= 1.9.3'
7
+
8
+ s.author = 'Manish Kangia'
9
+ s.email = 'info@vinsol.com'
10
+ s.homepage = 'http://vinsol.com'
11
+
12
+ s.summary = 'Provides the feature for a Spree store to allow user to club payment methods to pay for the order'
13
+ s.license = 'MIT'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.require_path = 'lib'
17
+ s.requirements << 'none'
18
+
19
+ s.add_dependency 'spree_core', '~> 2.1.0'
20
+
21
+ s.add_development_dependency 'database_cleaner'
22
+ s.add_development_dependency 'rspec-rails', '~> 2.13'
23
+ s.add_development_dependency 'simplecov'
24
+ s.add_development_dependency 'sqlite3'
25
+ end
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spree-split-payments
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Manish Kangia
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2014-04-01 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: spree_core
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 11
29
+ segments:
30
+ - 2
31
+ - 1
32
+ - 0
33
+ version: 2.1.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: database_cleaner
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec-rails
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ hash: 25
59
+ segments:
60
+ - 2
61
+ - 13
62
+ version: "2.13"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: simplecov
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: sqlite3
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ type: :development
92
+ version_requirements: *id005
93
+ description:
94
+ email: info@vinsol.com
95
+ executables: []
96
+
97
+ extensions: []
98
+
99
+ extra_rdoc_files: []
100
+
101
+ files:
102
+ - .gitignore
103
+ - .rspec
104
+ - .travis.yml
105
+ - Gemfile
106
+ - LICENSE
107
+ - README.md
108
+ - Rakefile
109
+ - app/assets/javascripts/admin/spree-split-payments.js
110
+ - app/assets/javascripts/store/spree-split-payments.js
111
+ - app/assets/stylesheets/admin/spree-split-payments.css
112
+ - app/assets/stylesheets/store/spree-split-payments.css
113
+ - app/controllers/spree/checkout_controller_decorator.rb
114
+ - app/models/spree/order_decorator.rb
115
+ - app/models/spree/payment_decorator.rb
116
+ - app/models/spree/payment_method_decorator.rb
117
+ - app/models/spree/user_decorator.rb
118
+ - app/overrides/add_partial_payment_form_fields_admin.rb
119
+ - app/overrides/add_split_payments_to_payment_form.rb
120
+ - app/views/shared/_split_payments.html.erb
121
+ - app/views/shared/_split_payments_data.html.erb
122
+ - bin/rails
123
+ - config/locales/en.yml
124
+ - config/routes.rb
125
+ - db/migrate/20140318063048_add_partial_payment_fields_to_spree_payment_method.rb
126
+ - db/migrate/20140318063049_add_is_partial_to_spree_payment.rb
127
+ - lib/generators/spree_split_payments/install/install_generator.rb
128
+ - lib/spree-split-payments.rb
129
+ - lib/spree_split_payments/engine.rb
130
+ - lib/spree_split_payments/factories.rb
131
+ - spec/controllers/spree/checkout_controller_spec.rb
132
+ - spec/models/spree/order_decorator_spec.rb
133
+ - spec/models/spree/payment_decorator_spec.rb
134
+ - spec/models/spree/payment_method_decorator_spec.rb
135
+ - spec/models/spree/user_decorator_spec.rb
136
+ - spec/spec_helper.rb
137
+ - spree-split-payments.gemspec
138
+ homepage: http://vinsol.com
139
+ licenses:
140
+ - MIT
141
+ post_install_message:
142
+ rdoc_options: []
143
+
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ hash: 53
152
+ segments:
153
+ - 1
154
+ - 9
155
+ - 3
156
+ version: 1.9.3
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ none: false
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ hash: 3
163
+ segments:
164
+ - 0
165
+ version: "0"
166
+ requirements:
167
+ - none
168
+ rubyforge_project:
169
+ rubygems_version: 1.8.24
170
+ signing_key:
171
+ specification_version: 3
172
+ summary: Provides the feature for a Spree store to allow user to club payment methods to pay for the order
173
+ test_files: []
174
+