spree_wallet 2.0.7 → 2.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/LICENSE +26 -0
  2. data/README.md +39 -0
  3. data/app/assets/stylesheets/store/checkout.css +1 -0
  4. data/app/assets/stylesheets/store/spree_wallet.css +3 -0
  5. data/app/controllers/spree/admin/store_credits_controller.rb +56 -0
  6. data/app/controllers/spree/checkout_controller_decorator.rb +57 -0
  7. data/app/controllers/spree/payments_controller_decorator.rb +34 -0
  8. data/app/models/spree/credit.rb +13 -0
  9. data/app/models/spree/debit.rb +13 -0
  10. data/app/models/spree/order_decorator.rb +53 -0
  11. data/app/models/spree/payment_decorator.rb +39 -0
  12. data/app/models/spree/payment_method/wallet.rb +19 -0
  13. data/app/models/spree/store_credit.rb +30 -0
  14. data/app/models/spree/user_decorator.rb +3 -0
  15. data/app/overrides/add_store_credits_tab_in_configuration_menu.rb +8 -0
  16. data/app/views/spree/admin/payments/source_forms/_wallet.html.erb +0 -0
  17. data/app/views/spree/admin/payments/source_views/_wallet.html.erb +0 -0
  18. data/app/views/spree/admin/store_credits/index.html.erb +97 -0
  19. data/app/views/spree/admin/store_credits/new.html.erb +27 -0
  20. data/app/views/spree/admin/users/index.html.erb +59 -0
  21. data/app/views/spree/checkout/_payment.html.erb +45 -0
  22. data/app/views/spree/checkout/payment/_wallet.html.erb +0 -0
  23. data/config/locales/en.yml +21 -0
  24. data/config/routes.rb +11 -0
  25. data/db/migrate/20130712094651_create_spree_store_credits.rb +15 -0
  26. data/db/migrate/20130715130434_add_store_credits_to_spree_users.rb +5 -0
  27. data/db/migrate/20130719124429_add_indexes_on_spree_store_credits_fields_type_and_user_id.rb +6 -0
  28. data/db/migrate/20130729071647_add_lock_version_to_spree_users.rb +5 -0
  29. data/db/migrate/20130729140933_add_unique_index_on_transaction_id_to_spree_store_credits.rb +5 -0
  30. data/db/migrate/20130801135105_add_transactioner_id_to_spree_store_credits.rb +6 -0
  31. data/lib/generators/spree_wallet/install/install_generator.rb +25 -0
  32. data/lib/spree/disable_negative_payment_mode_and_set_balance_ability.rb +18 -0
  33. data/lib/spree_wallet/engine.rb +26 -0
  34. data/lib/spree_wallet/factories.rb +6 -0
  35. data/lib/spree_wallet.rb +2 -0
  36. metadata +38 -3
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2013 [name of plugin creator]
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.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ SpreeWallet
2
+ ===========
3
+
4
+ Installation
5
+ ------------
6
+
7
+ Add spree_wallet to your Gemfile:
8
+
9
+ ```ruby
10
+
11
+ gem 'spree_wallet', :git => 'git://github.com/vinsol/spree_wallet.git'
12
+ OR
13
+ gem 'spree_wallet', '~> 2.0.6'
14
+ ```
15
+
16
+ Bundle your dependencies and run the installation generator:
17
+
18
+ ```shell
19
+ bundle
20
+ bundle exec rails g spree_wallet:install
21
+ bundle exec rake db:migrate
22
+ ```
23
+
24
+ Usage
25
+ -----
26
+
27
+ From Admin end, create a payment method of Wallet type. From User end, user can only select or unselect wallet payment type. Spree Wallet will deduce minimum of order total and spree wallet balance of that user. If there's any remaining amount in the order it will be deducted from other payment method choosen by the user.
28
+
29
+ While from admin end, Admin can select any amount from wallet, but it should be less than amount present in user's balance.
30
+
31
+ ## Contribution
32
+
33
+ You need to do a quick one-time creation of a test application and then you can use it to run the tests.
34
+
35
+ bundle exec rake test_app
36
+
37
+ Then run the rspec tests with mysql.
38
+
39
+ bundle exec rspec .
@@ -0,0 +1 @@
1
+ .hidden { display: none; }
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require store/checkout
3
+ */
@@ -0,0 +1,56 @@
1
+ module Spree
2
+ module Admin
3
+ class StoreCreditsController < ResourceController
4
+ helper_method :store_credit_class
5
+ skip_before_filter :load_resource, :only => [:index]
6
+ before_filter :disable_negative_payment_mode, :add_transactioner_to_store_credit, :only => :create
7
+
8
+ def index
9
+ @search = association_or_class.order_created_at_desc.ransack(params[:q])
10
+ @store_credits = @search.result.page(params[:page]).includes(required_includes_arguements)
11
+ end
12
+
13
+ protected
14
+
15
+ def disable_negative_payment_mode
16
+ @store_credit.disable_negative_payment_mode = true
17
+ end
18
+
19
+ def add_transactioner_to_store_credit
20
+ @store_credit.transactioner = spree_current_user
21
+ end
22
+
23
+ def self.parent_data
24
+ { :model_class => Spree.user_class, :find_by => :id, :model_name => 'spree/user' }
25
+ end
26
+
27
+ def association_or_class
28
+ parent ? parent.store_credits : Spree::StoreCredit
29
+ end
30
+
31
+ def required_includes_arguements
32
+ parent ? :transactioner : [:transactioner, :user]
33
+ end
34
+
35
+ def build_resource
36
+ if parent_data.present?
37
+ store_credit_class.new{ |store_credit| store_credit.user = parent }
38
+ else
39
+ store_credit_class.new
40
+ end
41
+ end
42
+
43
+ def find_resource
44
+ if parent_data.present?
45
+ store_credit_class.where(:id => params[:id], :user_id => parent.id).first
46
+ else
47
+ store_credit_class.where(:id => params[:id]).first
48
+ end
49
+ end
50
+
51
+ def store_credit_class
52
+ params[:type] ? "spree/#{params[:type]}".classify.constantize : model_class
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,57 @@
1
+ Spree::CheckoutController.class_eval do
2
+ before_filter :validate_payments, :only => :update, :if => lambda { @order && @order.has_checkout_step?("payment") && @order.payment? && @order.available_wallet_payment_method }
3
+
4
+ private
5
+ def object_params
6
+ if @order.has_checkout_step?("payment") && @order.payment?
7
+ if params[:payment_source].present?
8
+ source_params = params.delete(:payment_source)[non_wallet_payment_method]
9
+
10
+ if source_params
11
+ non_wallet_payment_attributes(params[:order][:payments_attributes]).first[:source_attributes] = source_params
12
+ end
13
+ end
14
+
15
+ if (params[:order][:payments_attributes])
16
+ # This method is overrided because spree add all order total in first payment, now after wallet we can have multiple payments.
17
+ if spree_current_user && @order.available_wallet_payment_method
18
+ wallet_payments = wallet_payment_attributes(params[:order][:payments_attributes])
19
+ wallet_payments.first[:amount] = [@order.remaining_total, spree_current_user.store_credits_total].min if wallet_payments.present?
20
+ params[:order][:payments_attributes] = wallet_payments if remaining_order_total_after_wallet(@order, wallet_payments) <= 0
21
+ non_wallet_payment_attributes(params[:order][:payments_attributes]).first[:amount] = remaining_order_total_after_wallet(@order, wallet_payments) if non_wallet_payment_attributes(params[:order][:payments_attributes]).present?
22
+ else
23
+ params[:order][:payments_attributes].first[:amount] = @order.remaining_total
24
+ end
25
+ end
26
+ end
27
+ params[:order]
28
+ end
29
+
30
+ def validate_payments
31
+ payments_attributes = params[:order][:payments_attributes]
32
+ wallet_payment = wallet_payment_attributes(payments_attributes)
33
+ if !spree_current_user && wallet_payment.present?
34
+ flash[:error] = Spree.t(:cannot_select_wallet_while_guest_checkout)
35
+ redirect_to checkout_state_path(@order.state)
36
+ elsif wallet_payment.present? && non_wallet_payment_attributes(payments_attributes).empty? && @order.remaining_total >= spree_current_user.store_credits_total
37
+ flash[:error] = Spree.t(:not_sufficient_amount_in_wallet)
38
+ redirect_to checkout_state_path(@order.state)
39
+ end
40
+ end
41
+
42
+ def non_wallet_payment_method
43
+ non_wallet_payment_attributes(params[:order][:payments_attributes]).first[:payment_method_id] if non_wallet_payment_attributes(params[:order][:payments_attributes]).first
44
+ end
45
+
46
+ def remaining_order_total_after_wallet(order, wallet_payments)
47
+ wallet_payments.present? ? order.remaining_total - wallet_payments.first[:amount] : order.remaining_total
48
+ end
49
+
50
+ def wallet_payment_attributes(payment_attributes)
51
+ payment_attributes.select { |payment| payment["payment_method_id"] == Spree::PaymentMethod::Wallet.first.id.to_s }
52
+ end
53
+
54
+ def non_wallet_payment_attributes(payment_attributes)
55
+ @non_wallet_payment ||= payment_attributes - wallet_payment_attributes(payment_attributes)
56
+ end
57
+ end
@@ -0,0 +1,34 @@
1
+ Spree::Admin::PaymentsController.class_eval do
2
+ def create
3
+ @payment = @order.payments.build(object_params)
4
+ if @payment.payment_method.is_a?(Spree::Gateway) && @payment.payment_method.payment_profiles_supported? && params[:card].present? and params[:card] != 'new'
5
+ @payment.source = Spree::CreditCard.find_by_id(params[:card])
6
+ end
7
+
8
+ begin
9
+ unless @payment.save
10
+ # In spree, if payment is not able to save. It will redirect to index page, rather than it should render new with errors.
11
+ render :new
12
+ return
13
+ end
14
+
15
+ if @order.completed?
16
+ @payment.process!
17
+ flash[:success] = flash_message_for(@payment, :successfully_created)
18
+
19
+ redirect_to admin_order_payments_path(@order)
20
+ else
21
+ #This is the first payment (admin created order)
22
+ until @order.completed?
23
+ @order.next!
24
+ end
25
+ flash[:success] = Spree.t(:new_order_completed)
26
+ redirect_to edit_admin_order_url(@order)
27
+ end
28
+
29
+ rescue Spree::Core::GatewayError => e
30
+ flash[:error] = "#{e.message}"
31
+ redirect_to new_admin_order_payment_path(@order)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ module Spree
2
+ class Credit < StoreCredit
3
+ # Negative Payment Mode cannot be set manually. They are reserved for some particular task internally.
4
+ PAYMENT_MODE = { 'Payment Refund' => -1, 'Refund' => 0, 'Bank' => 1 }
5
+
6
+ include Spree::DisableNegativePaymentModeAndSetBalanceAbility
7
+
8
+ private
9
+ def effective_amount(amount = amount)
10
+ amount
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Spree
2
+ class Debit < StoreCredit
3
+ # Negative Payment Mode cannot be set manually. They are reserved for some particular task internally.
4
+ PAYMENT_MODE = { 'Order Purchase' => -1, 'Deduce' => 0 }
5
+
6
+ include Spree::DisableNegativePaymentModeAndSetBalanceAbility
7
+
8
+ private
9
+ def effective_amount(amount = amount)
10
+ -(amount)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ Spree::Order.class_eval do
2
+ fsm = self.state_machines[:state]
3
+ fsm.after_transition :to => [:canceled], :do => :make_wallet_payments_void
4
+
5
+ def user_or_by_email
6
+ user ? user : Spree::User.where(:email => email).first
7
+ end
8
+
9
+ def make_wallet_payments_void
10
+ wallet_payments.each { |p| p.void! if p.can_void? }
11
+ end
12
+
13
+ def has_unprocessed_payments?
14
+ payments.with_state('checkout').reload.exists? || (available_wallet_payment_method.present? && (wallet_payment = payments.where(:payment_method_id => available_wallet_payment_method.id).last).present? && wallet_payment.amount <= remaining_total)
15
+ end
16
+
17
+ def remaining_total
18
+ total - payment_total
19
+ end
20
+
21
+ def available_payment_methods_without_wallet
22
+ available_payment_methods.reject { |p| p.is_a? Spree::PaymentMethod::Wallet }
23
+ end
24
+
25
+ def available_wallet_payment_method
26
+ @wallet_payment_method ||= available_payment_methods.select { |p| p.is_a? Spree::PaymentMethod::Wallet }.first
27
+ end
28
+
29
+ def other_than_wallet_payment_required?
30
+ remaining_total > user.store_credits_total
31
+ end
32
+
33
+ def available_wallet_payment_amount
34
+ [remaining_total, user_or_by_email.store_credits_total].min
35
+ end
36
+
37
+ def display_available_wallet_payment_amount
38
+ Spree::Money.new(available_wallet_payment_amount)
39
+ end
40
+
41
+ def remaining_total_after_wallet
42
+ remaining_total - available_wallet_payment_amount
43
+ end
44
+
45
+ def display_remaining_total_after_wallet
46
+ Spree::Money.new(remaining_total_after_wallet)
47
+ end
48
+
49
+ private
50
+ def wallet_payments
51
+ payments.where(:payment_method_id => Spree::PaymentMethod::Wallet.pluck(:id))
52
+ end
53
+ end
@@ -0,0 +1,39 @@
1
+ Spree::Payment.class_eval do
2
+ validates :amount, :numericality => { :less_than_or_equal_to => lambda { |payment| [payment.order_user_or_by_email.store_credits_total, payment.order_remaining_total].min }, :greater_than_or_equal_to => 0 }, :if => lambda { |payment| payment.order_user_or_by_email && payment.wallet? && payment.amount_changed? }, :allow_blank => true
3
+ validates :amount, :numericality => { :less_than_or_equal_to => lambda { |payment| payment.order_remaining_total }}, :unless => :wallet?, :if => :amount_changed?, :allow_blank => true
4
+ validate :restrict_wallet_when_no_user
5
+
6
+ after_create :complete!, :if => :wallet?
7
+
8
+ delegate :remaining_total, :user_or_by_email, :to => :order, :prefix => true, :allow_nil => true
9
+
10
+ fsm = self.state_machines[:state]
11
+ fsm.after_transition :from => fsm.states.map(&:name) - ['completed'], :to => ['completed'], :do => :consume_user_credits, :if => :wallet?
12
+ fsm.after_transition :from => ['completed'], :to => fsm.states.map(&:name) - ['completed'] , :do => :release_user_credits, :if => :wallet?
13
+
14
+ def wallet?
15
+ payment_method.is_a? Spree::PaymentMethod::Wallet
16
+ end
17
+
18
+ def invalidate_old_payments
19
+ order.payments.with_state('checkout').where("id != ?", self.id).each do |payment|
20
+ payment.invalidate!
21
+ end unless wallet?
22
+ end
23
+
24
+ private
25
+
26
+ def restrict_wallet_when_no_user
27
+ if wallet? && !order_user_or_by_email
28
+ self.errors[:base] = Spree.t(:wallet_not_linked_to_user)
29
+ end
30
+ end
31
+
32
+ def consume_user_credits
33
+ Spree::Debit.create!(:amount => amount, :payment_mode => Spree::Debit::PAYMENT_MODE['Order Purchase'], :reason => "Payment consumed of order #{order.number}") { |debit| debit.user = order_user_or_by_email }
34
+ end
35
+
36
+ def release_user_credits
37
+ Spree::Credit.create!(:amount => amount, :payment_mode => Spree::Credit::PAYMENT_MODE['Payment Refund'], :reason => "Payment released of order #{order.number}") { |credit| credit.user = order_user_or_by_email }
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ class PaymentMethod::Wallet < PaymentMethod
3
+ def actions
4
+ %w{void}
5
+ end
6
+
7
+ def can_void?(payment)
8
+ payment.state != 'void'
9
+ end
10
+
11
+ def void(*args)
12
+ ActiveMerchant::Billing::Response.new(true, "", {}, {})
13
+ end
14
+
15
+ def source_required?
16
+ false
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ class StoreCredit < ActiveRecord::Base
3
+ attr_accessible :amount, :reason, :payment_mode
4
+ attr_accessor :disable_negative_payment_mode
5
+
6
+ belongs_to :transactioner, :class_name => Spree.user_class
7
+
8
+ validates :amount, :user_id, :reason, :type, :balance, :transaction_id, :presence => true
9
+ validates :amount, :balance, :numericality => { :greater_than_or_equal_to => 0 }, :allow_blank => true
10
+ validates :transaction_id, :uniqueness => true, :allow_blank => true
11
+
12
+ belongs_to :user, :class_name => Spree.user_class
13
+
14
+ before_validation :generate_transaction_id, :on => :create
15
+ before_create :update_user_wallet
16
+
17
+ scope :order_created_at_desc, order('created_at desc')
18
+
19
+ private
20
+ def update_user_wallet
21
+ user.update_attribute(:store_credits_total, balance)
22
+ end
23
+
24
+ def generate_transaction_id
25
+ begin
26
+ self.transaction_id = (Time.now.strftime("%s") + rand(999999).to_s).to(15)
27
+ end while Spree::StoreCredit.where(:transaction_id => transaction_id).present?
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ Spree.user_class.class_eval do
2
+ has_many :store_credits, :class_name => 'Spree::StoreCredit', :dependent => :destroy
3
+ end
@@ -0,0 +1,8 @@
1
+ Deface::Override.new(
2
+ :virtual_path => 'spree/admin/shared/_configuration_menu',
3
+ :name => 'add_store_credits_to_admin_configuration_sidebar',
4
+ :insert_bottom => "[data-hook='admin_configurations_sidebar_menu']",
5
+ :text => %q{
6
+ <%= configurations_sidebar_menu_item Spree.t(:store_credits), admin_store_credits_path %>
7
+ }
8
+ )
@@ -0,0 +1,97 @@
1
+ <%= render :partial => 'spree/admin/shared/configuration_menu' %>
2
+
3
+ <% content_for :table_filter do %>
4
+ <div data-hook="admin_store_credits_sidebar">
5
+
6
+ <%= search_form_for [:admin, @search] do |f| %>
7
+ <div data-hook="admin_store_credits_index_search">
8
+ <div class="alpha nine columns">
9
+ <div class="field">
10
+ <%= f.label :transaction_id_eq, Spree.t(:transaction_id) %>
11
+ <%= f.text_field :transaction_id_eq %>
12
+ </div>
13
+
14
+ <div class="field">
15
+ <%= f.label :transactioner_email_cont, Spree.t(:transactioner_email) %>
16
+ <%= f.email_field :transactioner_email_cont %>
17
+ </div>
18
+
19
+ <% unless @user %>
20
+ <div class="field">
21
+ <%= f.label :user_email_cont, Spree.t(:user_email) %>
22
+ <%= f.email_field :user_email_cont %>
23
+ </div>
24
+ <% end %>
25
+ </div>
26
+ </div>
27
+
28
+ <div class="clear"></div>
29
+
30
+ <div class="form-buttons actions filter-actions" data-hook="admin_products_index_search_buttons">
31
+ <%= button Spree.t(:search), 'icon-search' %>
32
+ </div>
33
+ <% end %>
34
+ </div>
35
+ <% end %>
36
+
37
+ <% content_for :page_title do %>
38
+ <% if @user %>
39
+ <%= Spree.t(:store_credits_for_user_email, user_email: @user.email) %> (<%= Spree.t(:current_balance_for_user, total: @user.store_credits_total) %>)
40
+ <% else %>
41
+ <%= Spree.t(:listing_store_credits) %>
42
+ <% end %>
43
+ <% end %>
44
+
45
+ <% if @user %>
46
+ <% content_for :page_actions do %>
47
+ <li>
48
+ <%= button_link_to Spree.t(:new_credit), new_admin_user_credit_path(:user_id => @user) %>
49
+ <%= button_link_to Spree.t(:new_debit), new_admin_user_debit_path(:user_id => @user) %>
50
+ </li>
51
+ <% end %>
52
+ <% end %>
53
+
54
+ <%= paginate @store_credits %>
55
+
56
+ <table class="index">
57
+ <thead>
58
+ <tr>
59
+ <th><%= sort_link @search, :transaction_id, Spree.t(:transaction_number) %></th>
60
+ <% unless @user %>
61
+ <th><%= sort_link @search, :user_email, Spree.t(:user) %></th>
62
+ <% end %>
63
+ <th><%= sort_link @search, :transactioner_email, Spree.t(:transactioner_email) %></th>
64
+ <th><%= sort_link @search, :amount, Spree.t(:amount) %></th>
65
+ <th><%= sort_link @search, :balance, Spree.t(:balance) %></th>
66
+ <% if @user %>
67
+ <th><%= Spree.t(:reason) %></th>
68
+ <th><%= Spree.t(:payment_mode) %></th>
69
+ <% end %>
70
+ <th><%= sort_link @search, :type, Spree.t(:type) %></th>
71
+ <% unless @user %>
72
+ <th><%= Spree.t(:action) %></th>
73
+ <% end %>
74
+ </tr>
75
+ </thead>
76
+ <tbody>
77
+ <% @store_credits.each do |store_credit|%>
78
+ <tr id="<%= cycle('odd', 'even') %>">
79
+ <td class="align-center"><%= store_credit.transaction_id %></td>
80
+ <% unless @user %>
81
+ <td class="align-center"><%= link_to store_credit.user.email, admin_user_path(store_credit.user) %></td>
82
+ <% end %>
83
+ <td class="align-center"><%= store_credit.transactioner.try(:email) %></td>
84
+ <td class="align-center"><%= number_to_currency store_credit.amount %></td>
85
+ <td class="align-center"><%= number_to_currency store_credit.balance %></td>
86
+ <% if @user %>
87
+ <td class="align-center"><%= store_credit.reason %></td>
88
+ <td class="align-center"><%= store_credit.class::PAYMENT_MODE.key(store_credit.payment_mode) %>
89
+ <% end %>
90
+ <td class="align-center"><%= store_credit.type.demodulize %></td>
91
+ <% unless @user %>
92
+ <td><%= link_to Spree.t(:credit), new_admin_user_credit_path(:user_id => store_credit.user) %> | <%= link_to Spree.t(:debit), new_admin_user_debit_path(:user_id => store_credit.user) %> | <%= link_to Spree.t(:view), admin_user_store_credits_path(:user_id => store_credit.user) %></td>
93
+ <% end %>
94
+ </tr>
95
+ <% end %>
96
+ </tbody>
97
+ </table>
@@ -0,0 +1,27 @@
1
+ <%= render :partial => 'spree/admin/shared/configuration_menu' %>
2
+
3
+ <h1><%= Spree.t("new_store_#{@store_credit.type.demodulize.downcase}")%></h1>
4
+
5
+ <%= render 'spree/shared/error_messages', :target => @store_credit %>
6
+
7
+ <h3 data-hook='user-email'>User Email: <%= @store_credit.user.email %></h3>
8
+ <h4 data-hook='user-email'>Current Balance: <%= @store_credit.user.store_credits_total %></h4>
9
+
10
+ <%= form_for [:admin, @user, @store_credit], :as => :store_credit do |f| %>
11
+ <%= f.field_container :amount do %>
12
+ <%= f.label :amount %><br />
13
+ <%= f.text_field :amount, :id => "debit_credit_amount" %>
14
+ <% end %>
15
+
16
+ <%= f.field_container :payment_mode do %>
17
+ <%= f.label :payment_mode, 'Payment Mode' %><br />
18
+ <%= f.select :payment_mode, store_credit_class::PAYMENT_MODE.select { |key, value| value >= 0 }, { :prompt => 'Select' } %>
19
+ <% end %>
20
+
21
+ <%= f.field_container :reason do %>
22
+ <%= f.label :reason %><br />
23
+ <%= f.text_field :reason %>
24
+ <% end %>
25
+
26
+ <%= f.submit :commit, :value => @store_credit.type.demodulize, :class => 'button', :id => 'debit_credit' %>
27
+ <% end %>
@@ -0,0 +1,59 @@
1
+ <% content_for :page_title do %>
2
+ <%= Spree.t(:listing_users) %>
3
+ <% end %>
4
+
5
+ <% content_for :page_actions do %>
6
+ <li>
7
+ <%= button_link_to Spree.t(:new_user), new_object_url, :icon => 'icon-plus', :id => 'admin_new_user_link' %>
8
+ </li>
9
+ <% end %>
10
+
11
+ <table class="index" id="listing_users" data-hook>
12
+ <colgroup>
13
+ <col style="width: 40%">
14
+ <col style="width: 20%">
15
+ <col style="width: 25%">
16
+ <col style="width: 15%">
17
+ </colgroup>
18
+ <thead>
19
+ <tr data-hook="admin_users_index_headers">
20
+ <th><%= sort_link @search,:email, Spree.t(:user), {}, {:title => 'users_email_title'} %></th>
21
+ <th><%= Spree.t(:store_credits_total) %></th>
22
+ <th><%= Spree.t(:store_credits_action) %></th>
23
+ <th data-hook="admin_users_index_header_actions" class="actions"></th>
24
+ </tr>
25
+ </thead>
26
+ <tbody>
27
+ <% @users.each do |user|%>
28
+ <tr id="<%= spree_dom_id user %>" data-hook="admin_users_index_rows" class="<%= cycle('odd', 'even')%>">
29
+ <td class='user_email'><%=link_to user.email, object_url(user) %></td>
30
+ <td><%= user.store_credits_total %></td>
31
+ <td><%= link_to Spree.t(:credit), new_admin_user_credit_path(:user_id => user) %> | <%= link_to Spree.t(:debit), new_admin_user_debit_path(:user_id => user) %> | <%= link_to Spree.t(:view), admin_user_store_credits_path(:user_id => user) %></td>
32
+ <td data-hook="admin_users_index_row_actions" class="actions">
33
+ <%= link_to_edit user, :no_text => true %>
34
+ <%= link_to_delete user, :no_text => true %>
35
+ </td>
36
+ </tr>
37
+ <% end %>
38
+ </tbody>
39
+ </table>
40
+
41
+ <%= paginate @users %>
42
+
43
+ <% content_for :sidebar_title do %>
44
+ <%= Spree.t(:search) %>
45
+ <% end %>
46
+
47
+ <% content_for :sidebar do %>
48
+ <div class="box align-center" data-hook="admin_users_index_search">
49
+ <%= search_form_for [:admin, @search] do |f| %>
50
+ <div class="field">
51
+ <%= f.label Spree.t(:email) %> <br>
52
+ <%= f.text_field :email_cont, :class => 'fullwidth' %>
53
+ </div>
54
+ <div data-hook="admin_users_index_search_buttons">
55
+ <%= button Spree.t(:search), 'icon-search' %>
56
+ </div>
57
+ <% end %>
58
+ </div>
59
+ <% end %>
@@ -0,0 +1,45 @@
1
+ <fieldset id="payment" data-hook>
2
+ <legend align="center"><%= Spree.t(:payment_information) %></legend>
3
+ <div data-hook="checkout_payment_step">
4
+
5
+
6
+ <% if spree_current_user && @order.available_wallet_payment_method && !spree_current_user.store_credits_total.to_f.zero? %>
7
+ <%= check_box_tag "order[payments_attributes][][payment_method_id]", @order.available_wallet_payment_method.id %>
8
+ <%= Spree.t(@order.available_wallet_payment_method.name, :scope => :payment_methods, :default => @order.available_wallet_payment_method.name) %>( <%= spree_current_user.store_credits_total %> )
9
+ <% end %>
10
+
11
+ <% @order.available_payment_methods_without_wallet.each do |method| %>
12
+ <p>
13
+ <label>
14
+ <%= radio_button_tag "order[payments_attributes][][payment_method_id]", method.id %>
15
+ <%= Spree.t(method.name, :scope => :payment_methods, :default => method.name) %>
16
+ </label>
17
+ </p>
18
+ <% end %>
19
+
20
+ <ul id="payment-methods" data-hook>
21
+ <% @order.available_payment_methods_without_wallet.each do |method| %>
22
+ <!-- hidden class is added to hide the partial initially -->
23
+ <li id="payment_method_<%= method.id %>" class="hidden <%= 'last' if method == @order.available_payment_methods_without_wallet.last %>" data-hook>
24
+ <fieldset>
25
+ <%= render :partial => "spree/checkout/payment/#{method.method_type}", :locals => { :payment_method => method } %>
26
+ </fieldset>
27
+ </li>
28
+ <% end %>
29
+ </ul>
30
+ <br style="clear:both;" />
31
+ <% if Spree::Promotion.with_code.count > 0 %>
32
+ <p class='field' data-hook='coupon_code'>
33
+ <%= form.label :coupon_code %><br />
34
+ <%= form.text_field :coupon_code %>
35
+ </p>
36
+ <% end %>
37
+ </div>
38
+ </fieldset>
39
+
40
+ <br class="space" />
41
+
42
+ <div class="form-buttons" data-hook="buttons">
43
+ <%= submit_tag Spree.t(:save_and_continue), :class => 'continue button primary' %>
44
+ <script>Spree.disableSaveOnClick();</script>
45
+ </div>
File without changes
@@ -0,0 +1,21 @@
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
+ spree:
6
+ store_credits_action: "Store Credits Action"
7
+ listing_store_credits: "Listing Store Credits"
8
+ store_credits: "Store Credits"
9
+ new_credit: "New Credit"
10
+ new_debit: "New Debit"
11
+ credit: "Credit"
12
+ debit: "Debit"
13
+ view: "View"
14
+ store_credits_for_user_email: "Store Credits For %{user_email}"
15
+ not_sufficient_amount_in_wallet: "Payment could not be processed. Because you don't have sufficient amount in your wallet. Please choose another payment method with it."
16
+ new_store_credit: "New Store Credit"
17
+ new_store_debit: "New Store Debit"
18
+ cannot_select_wallet_while_guest_checkout: "Payment Method Wallet can't be selected while guest checkout."
19
+ wallet_not_linked_to_user: "There is no wallet linked to the user of this order"
20
+ current_balance_for_user: "Current Balance: %{total}"
21
+ store_credits_total: "Store Credits Total"
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ namespace :admin do
3
+ resources :users, :only => :none do
4
+ resources :store_credits, :only => [:index]
5
+ resources :credits, :only => [:new, :create, :show], :controller => :store_credits, :type => 'credits'
6
+ resources :debits, :only => [:new, :create, :show], :controller => :store_credits, :type => 'debits'
7
+ end
8
+
9
+ resources :store_credits, :only => [:index]
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ class CreateSpreeStoreCredits < ActiveRecord::Migration
2
+ def change
3
+ create_table :spree_store_credits do |t|
4
+ t.string :reason
5
+ t.string :type
6
+ t.string :transaction_id
7
+ t.decimal :amount, :precision => 10, :scale => 2
8
+ t.decimal :balance, :precision => 10, :scale => 2
9
+ t.integer :payment_mode
10
+ t.references :user
11
+
12
+ t.timestamps
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ class AddStoreCreditsToSpreeUsers < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_users, :store_credits_total, :decimal, :precision => 10, :scale => 2, :default => 0.0
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddIndexesOnSpreeStoreCreditsFieldsTypeAndUserId < ActiveRecord::Migration
2
+ def change
3
+ add_index :spree_store_credits, :type, :name => "index_type_on_spree_store_credits"
4
+ add_index :spree_store_credits, :user_id, :name => "index_user_id_on_spree_store_credits"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddLockVersionToSpreeUsers < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_users, :lock_version, :integer, :default => 0
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddUniqueIndexOnTransactionIdToSpreeStoreCredits < ActiveRecord::Migration
2
+ def change
3
+ add_index "spree_store_credits", "transaction_id", :name => "index_spree_store_credits_on_transaction_id", :unique => true
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddTransactionerIdToSpreeStoreCredits < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_store_credits, :transactioner_id, :integer
4
+ add_index :spree_store_credits, :transactioner_id
5
+ end
6
+ end
@@ -0,0 +1,25 @@
1
+ module SpreeWallet
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ class_option :auto_run_migrations, :type => :boolean, :default => false
6
+
7
+ def add_stylesheets
8
+ inject_into_file 'app/assets/stylesheets/store/all.css', " *= require store/spree_wallet\n", :before => /\*\//, :verbose => true
9
+ end
10
+
11
+ def add_migrations
12
+ run 'bundle exec rake railties:install:migrations FROM=spree_wallet'
13
+ end
14
+
15
+ def run_migrations
16
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
17
+ if run_migrations
18
+ run 'bundle exec rake db:migrate'
19
+ else
20
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ module DisableNegativePaymentModeAndSetBalanceAbility
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ attr_accessor :disable_negative_payment_mode
6
+
7
+ validates :payment_mode, :inclusion => { :in => klass::PAYMENT_MODE.values }, :unless => :disable_negative_payment_mode
8
+ validates :payment_mode, :inclusion => { :in => klass::PAYMENT_MODE.values.select { |value| value >= 0 } }, :if => :disable_negative_payment_mode
9
+
10
+ before_validation :set_balance, :on => :create
11
+ end
12
+ end
13
+
14
+ def set_balance
15
+ self.balance = user.store_credits_total + effective_amount if user && amount
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ module SpreeWallet
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_wallet'
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
+ end
19
+
20
+ initializer "spree.register.payment_methods" do |app|
21
+ app.config.spree.payment_methods << Spree::PaymentMethod::Wallet
22
+ end
23
+
24
+ config.to_prepare &method(:activate).to_proc
25
+ end
26
+ 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_wallet/factories'
6
+ end
@@ -0,0 +1,2 @@
1
+ require 'spree_core'
2
+ require 'spree_wallet/engine'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_wallet
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.7
4
+ version: 2.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-01-14 00:00:00.000000000 Z
12
+ date: 2014-01-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: spree_core
@@ -32,7 +32,42 @@ email: info@vinsol.com
32
32
  executables: []
33
33
  extensions: []
34
34
  extra_rdoc_files: []
35
- files: []
35
+ files:
36
+ - LICENSE
37
+ - README.md
38
+ - app/assets/stylesheets/store/checkout.css
39
+ - app/assets/stylesheets/store/spree_wallet.css
40
+ - app/overrides/add_store_credits_tab_in_configuration_menu.rb
41
+ - app/models/spree/user_decorator.rb
42
+ - app/models/spree/payment_method/wallet.rb
43
+ - app/models/spree/payment_decorator.rb
44
+ - app/models/spree/store_credit.rb
45
+ - app/models/spree/debit.rb
46
+ - app/models/spree/credit.rb
47
+ - app/models/spree/order_decorator.rb
48
+ - app/controllers/spree/checkout_controller_decorator.rb
49
+ - app/controllers/spree/admin/store_credits_controller.rb
50
+ - app/controllers/spree/payments_controller_decorator.rb
51
+ - app/views/spree/checkout/payment/_wallet.html.erb
52
+ - app/views/spree/checkout/_payment.html.erb
53
+ - app/views/spree/admin/users/index.html.erb
54
+ - app/views/spree/admin/store_credits/new.html.erb
55
+ - app/views/spree/admin/store_credits/index.html.erb
56
+ - app/views/spree/admin/payments/source_views/_wallet.html.erb
57
+ - app/views/spree/admin/payments/source_forms/_wallet.html.erb
58
+ - config/routes.rb
59
+ - config/locales/en.yml
60
+ - lib/generators/spree_wallet/install/install_generator.rb
61
+ - lib/spree/disable_negative_payment_mode_and_set_balance_ability.rb
62
+ - lib/spree_wallet.rb
63
+ - lib/spree_wallet/engine.rb
64
+ - lib/spree_wallet/factories.rb
65
+ - db/migrate/20130729071647_add_lock_version_to_spree_users.rb
66
+ - db/migrate/20130719124429_add_indexes_on_spree_store_credits_fields_type_and_user_id.rb
67
+ - db/migrate/20130712094651_create_spree_store_credits.rb
68
+ - db/migrate/20130729140933_add_unique_index_on_transaction_id_to_spree_store_credits.rb
69
+ - db/migrate/20130715130434_add_store_credits_to_spree_users.rb
70
+ - db/migrate/20130801135105_add_transactioner_id_to_spree_store_credits.rb
36
71
  homepage: http://vinsol.com
37
72
  licenses:
38
73
  - MIT