solidus_virtual_gift_card 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/app/assets/javascripts/spree/backend/solidus_virtual_gift_card.coffee +6 -6
  4. data/app/assets/stylesheets/spree/backend/solidus_virtual_gift_card.css +4 -0
  5. data/app/controllers/spree/admin/gift_cards_controller.rb +32 -12
  6. data/app/mailers/spree/gift_card_mailer.rb +2 -1
  7. data/app/models/concerns/spree/gift_cards/line_item_concerns.rb +6 -1
  8. data/app/models/concerns/spree/gift_cards/order_concerns.rb +15 -9
  9. data/app/models/concerns/spree/gift_cards/order_contents_concerns.rb +84 -0
  10. data/app/models/spree/order_contents_decorator.rb +1 -0
  11. data/app/models/spree/virtual_gift_card.rb +47 -10
  12. data/app/overrides/admin_gift_card_order_confirmation.rb +6 -0
  13. data/app/overrides/admin_item_view_gift_card_codes.rb +9 -2
  14. data/app/views/spree/admin/gift_cards/_lookup_form.html.erb +31 -6
  15. data/app/views/spree/admin/gift_cards/edit.html.erb +81 -0
  16. data/app/views/spree/admin/gift_cards/index.html.erb +39 -0
  17. data/app/views/spree/admin/gift_cards/show.html.erb +0 -32
  18. data/app/views/spree/admin/orders/_cart_gift_card_details.html.erb +3 -0
  19. data/app/views/spree/admin/orders/_confirmation_gift_card_details.html.erb +50 -0
  20. data/app/views/spree/admin/orders/_gift_card_details.html.erb +2 -0
  21. data/app/views/spree/admin/orders/_shipments_gift_card_details.html.erb +3 -0
  22. data/bin/rails +1 -1
  23. data/config/initializers/line_item_controller_gift_card_details.rb +4 -0
  24. data/config/initializers/ransack.rb +7 -0
  25. data/config/locales/en.yml +3 -0
  26. data/config/routes.rb +9 -1
  27. data/db/migrate/20151013172931_add_recipient_fields_to_virtual_gift_card.rb +8 -0
  28. data/db/migrate/20151013210615_add_active_flag_to_virtual_gift_card.rb +5 -0
  29. data/db/migrate/20151013214647_set_redeemable_true_on_virtual_gift_cards.rb +11 -0
  30. data/db/migrate/20151019190731_add_email_send_time_to_virtual_gift_card.rb +7 -0
  31. data/lib/spree_virtual_gift_card/factories.rb +11 -2
  32. data/lib/tasks/send_gift_card_emails.rake +8 -0
  33. data/solidus_virtual_gift_card.gemspec +1 -1
  34. data/spec/controllers/spree/admin/gift_cards_controller_spec.rb +1 -33
  35. data/spec/controllers/spree/api/gift_cards_controller_spec.rb +1 -1
  36. data/spec/features/admin/gift_cards_spec.rb +74 -0
  37. data/spec/lib/tasks/send_gift_card_emails_spec.rb +60 -0
  38. data/spec/models/spree/line_item_spec.rb +21 -0
  39. data/spec/models/spree/order_contents_spec.rb +270 -0
  40. data/spec/models/spree/order_spec.rb +34 -57
  41. data/spec/models/spree/virtual_gift_card_spec.rb +54 -23
  42. data/spec/spec_helper.rb +1 -0
  43. metadata +23 -3
  44. data/app/views/spree/admin/orders/_gift_card_redemption_codes.html.erb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ead961e2e91055078cda6622eff9bcb37135679c
4
- data.tar.gz: fc2ae98ff7e39233c92aa1ef57e5b88647baec14
3
+ metadata.gz: d68ab9a6fbe5f3e13073ab38d343df394def740a
4
+ data.tar.gz: 1ef7434bca9095d79f2889e46232725d85d6aab5
5
5
  SHA512:
6
- metadata.gz: 70cff66f119830a384728feb0b4a127f4fd86aa59189662d7c93c913e7e3422d9e72a054a89c7b555dbac3d569b49bdbe75d9a38ca30ffeacc82d36653b36ef7
7
- data.tar.gz: 95b4b982fdba785b3d6a08a87a1ea8c9a366f2d1fb688dcea8b2f0bd30b9f98334a04e511d556340f625ba91b211dd2497fe3439b905db9dfc79fffb8ecc611a
6
+ metadata.gz: 7d2212e47e0a8463e009165fa50c4eaa27ec63f4b131c874919c3b479f18454d0012199a6d4034e78c5d161426eade5176d0c41c27a99cd584d72618a81fb371
7
+ data.tar.gz: 9636919f4bfbb68ff3b34509b952971d113147e47a61de9490a73b1c3c47d326a0e2dce7a60bf26bbc34bb52ed719c1f9b41a50162287a142775b04118235fb1
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ gem "solidus_auth_devise", "~> 1.0"
5
5
 
6
6
  group :test, :development do
7
7
  gem "pry-byebug"
8
+ gem 'selenium-webdriver'
8
9
  end
9
10
 
10
11
  gemspec
@@ -1,9 +1,9 @@
1
1
  window.GiftCards =
2
- _bindLookupGiftCard: ->
3
- $(document).on('submit', '#lookup-redemption-code', (event) ->
4
- event.preventDefault()
5
- window.location.href = $(this).attr('action') + '/' + $(this).find('#gift_card_redemption_code').val()
2
+ setAdminDatepicker: () ->
3
+ $(document).ready(() ->
4
+ if $(".giftcard-datepicker").val()
5
+ $(".giftcard-datepicker").datepicker("setDate", new Date($(".giftcard-datepicker").val().split("-")))
6
+ $(".giftcard-datepicker").datepicker("option", "minDate", new Date())
6
7
  )
7
8
 
8
- init: ->
9
- @_bindLookupGiftCard()
9
+
@@ -5,3 +5,7 @@
5
5
  .lookup-gift-card-button {
6
6
  margin-top: 15px;
7
7
  }
8
+
9
+ .last-email-sent {
10
+ margin-bottom: 10px;
11
+ }
@@ -1,17 +1,30 @@
1
1
  class Spree::Admin::GiftCardsController < Spree::Admin::BaseController
2
- before_filter :load_gift_card_history, only: [:show]
3
2
  before_filter :load_user, only: [:lookup, :redeem]
4
3
  before_filter :load_gift_card_for_redemption, only: [:redeem]
4
+ before_filter :load_gift_card_by_id, only: [:edit, :update, :send_email]
5
+ before_filter :load_order, only: [:edit, :update]
5
6
 
6
7
  def index
8
+ @search = Spree::VirtualGiftCard.purchased.search(params[:q])
9
+ @gift_cards = @search.result.page(params[:page]).per(params[:per_page])
7
10
  end
8
11
 
9
- def show
12
+ def edit
10
13
  end
11
14
 
12
15
  def lookup
13
16
  end
14
17
 
18
+ def update
19
+ if @gift_card.update_attributes(gift_card_params)
20
+ flash[:success] = Spree.t("admin.gift_cards.gift_card_updated")
21
+ redirect_to edit_admin_order_path(@order)
22
+ else
23
+ flash[:error] = @gift_card.errors.full_messages.join(", ")
24
+ redirect_to :back
25
+ end
26
+ end
27
+
15
28
  def redeem
16
29
  if @gift_card.redeem(@user)
17
30
  flash[:success] = Spree.t("admin.gift_cards.redeemed_gift_card")
@@ -22,18 +35,13 @@ class Spree::Admin::GiftCardsController < Spree::Admin::BaseController
22
35
  end
23
36
  end
24
37
 
25
- private
26
-
27
- def load_gift_card_history
28
- redemption_code = Spree::RedemptionCodeGenerator.format_redemption_code_for_lookup(params[:id])
29
- @gift_cards = Spree::VirtualGiftCard.where(redemption_code: redemption_code)
30
-
31
- if @gift_cards.empty?
32
- flash[:error] = Spree.t('admin.gift_cards.errors.not_found')
33
- redirect_to(admin_gift_cards_path)
34
- end
38
+ def send_email
39
+ @gift_card.send_email
40
+ redirect_to :back
35
41
  end
36
42
 
43
+ private
44
+
37
45
  def load_gift_card_for_redemption
38
46
  redemption_code = Spree::RedemptionCodeGenerator.format_redemption_code_for_lookup(params[:gift_card][:redemption_code])
39
47
  @gift_card = Spree::VirtualGiftCard.active_by_redemption_code(redemption_code)
@@ -44,10 +52,22 @@ class Spree::Admin::GiftCardsController < Spree::Admin::BaseController
44
52
  end
45
53
  end
46
54
 
55
+ def load_gift_card_by_id
56
+ @gift_card = Spree::VirtualGiftCard.find_by(id: params[:id])
57
+ end
58
+
59
+ def load_order
60
+ @order = Spree::Order.find_by(number: params[:order_id])
61
+ end
62
+
47
63
  def load_user
48
64
  @user = Spree::User.find(params[:user_id])
49
65
  end
50
66
 
67
+ def gift_card_params
68
+ params.require(:virtual_gift_card).permit(:recipient_name, :recipient_email, :purchaser_name, :gift_message, :send_email_at)
69
+ end
70
+
51
71
  def model_class
52
72
  Spree::VirtualGiftCard
53
73
  end
@@ -2,7 +2,8 @@ class Spree::GiftCardMailer < Spree::BaseMailer
2
2
  def gift_card_email(gift_card)
3
3
  @gift_card = gift_card.respond_to?(:id) ? gift_card : Spree::VirtualGiftCard.find(gift_card)
4
4
  @order = @gift_card.line_item.order
5
+ send_to_address = @gift_card.recipient_email || @order.email
5
6
  subject = "#{Spree::Store.current.name} #{Spree.t('gift_card_mailer.gift_card_email.subject')}"
6
- mail(to: @order.email, from: from_address, subject: subject)
7
+ mail(to: send_to_address, from: from_address, subject: subject)
7
8
  end
8
9
  end
@@ -3,8 +3,9 @@ module Spree
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- has_many :gift_cards, class_name: Spree::VirtualGiftCard
6
+ has_many :gift_cards, class_name: Spree::VirtualGiftCard, dependent: :destroy
7
7
  delegate :gift_card?, :gift_card, to: :product
8
+ self.whitelisted_ransackable_associations += %w[order]
8
9
  prepend(InstanceMethods)
9
10
  end
10
11
 
@@ -12,6 +13,10 @@ module Spree
12
13
  def redemption_codes
13
14
  gift_cards.map {|gc| {amount: gc.formatted_amount, redemption_code: gc.formatted_redemption_code}}
14
15
  end
16
+
17
+ def gift_card_details
18
+ gift_cards.map(&:details)
19
+ end
15
20
  end
16
21
  end
17
22
  end
@@ -11,22 +11,28 @@ module Spree
11
11
  end
12
12
 
13
13
  module InstanceMethods
14
- def finalize!
15
- create_gift_cards
16
- super
14
+ def gift_card_match(line_item, options)
15
+ return true unless line_item.gift_card?
16
+ return true unless options["gift_card_details"]
17
+ line_item.gift_cards.any? do |gift_card|
18
+ gc_detail_set = gift_card.details.stringify_keys.except("send_email_at").to_set
19
+ options_set = options["gift_card_details"].except("send_email_at").to_set
20
+ gc_detail_set.superset?(options_set)
21
+ end
17
22
  end
18
23
 
19
- def create_gift_cards
20
- line_items.each do |item|
21
- item.quantity.times do
22
- Spree::VirtualGiftCard.create!(amount: item.price, currency: item.currency, purchaser: user, line_item: item) if item.gift_card?
23
- end
24
+ def finalize!
25
+ super
26
+ gift_cards.each do |gift_card|
27
+ gift_card.make_redeemable!(purchaser: user)
24
28
  end
25
29
  end
26
30
 
27
31
  def send_gift_card_emails
28
32
  gift_cards.each do |gift_card|
29
- Spree::GiftCardMailer.gift_card_email(gift_card).deliver
33
+ if gift_card.send_email_at.nil? || gift_card.send_email_at <= DateTime.now
34
+ gift_card.send_email
35
+ end
30
36
  end
31
37
  end
32
38
  end
@@ -0,0 +1,84 @@
1
+ module Spree
2
+ module GiftCards::OrderContentsConcerns
3
+ extend ActiveSupport::Concern
4
+ class GiftCardDateFormatError < StandardError; end
5
+
6
+ included do
7
+ prepend(InstanceMethods)
8
+ end
9
+
10
+ module InstanceMethods
11
+ def add(variant, quantity = 1, options = {})
12
+ line_item = super
13
+ create_gift_cards(line_item, quantity, options["gift_card_details"] || {})
14
+ line_item
15
+ end
16
+
17
+ def remove(variant, quantity = 1, options = {})
18
+ line_item = super
19
+ remove_gift_cards(line_item, quantity)
20
+ line_item
21
+ end
22
+
23
+ def update_cart(params)
24
+ update_success = super(params)
25
+
26
+ if update_success && params[:line_items_attributes]
27
+ line_item = Spree::LineItem.find_by(id: params[:line_items_attributes][:id])
28
+ new_quantity = params[:line_items_attributes][:quantity].to_i
29
+ update_gift_cards(line_item, new_quantity)
30
+ end
31
+
32
+ update_success
33
+ end
34
+
35
+ private
36
+
37
+ def create_gift_cards(line_item, quantity_diff, gift_card_details = {})
38
+ if line_item.gift_card?
39
+ quantity_diff.to_i.times do
40
+ Spree::VirtualGiftCard.create!(
41
+ amount: line_item.price,
42
+ currency: line_item.currency,
43
+ line_item: line_item,
44
+ recipient_name: gift_card_details["recipient_name"],
45
+ recipient_email: gift_card_details["recipient_email"],
46
+ purchaser_name: gift_card_details["purchaser_name"],
47
+ gift_message: gift_card_details["gift_message"],
48
+ send_email_at: format_date(gift_card_details["send_email_at"])
49
+ )
50
+ end
51
+ end
52
+ end
53
+
54
+ def remove_gift_cards(line_item, quantity_diff)
55
+ if line_item.gift_card?
56
+ line_item.gift_cards.order(:created_at).last(quantity_diff).each(&:destroy!)
57
+ end
58
+ end
59
+
60
+ def update_gift_cards(line_item, new_quantity)
61
+ if line_item && line_item.gift_card?
62
+ gift_card_count = line_item.gift_cards.count
63
+ if new_quantity > gift_card_count
64
+ create_gift_cards(line_item, new_quantity - gift_card_count)
65
+ elsif new_quantity < line_item.gift_cards.count
66
+ remove_gift_cards(line_item, gift_card_count - new_quantity)
67
+ end
68
+ end
69
+ end
70
+
71
+ def format_date(date)
72
+ return date if date.acts_like?(:date) || date.acts_like?(:time)
73
+ return Date.today if date.nil?
74
+
75
+ begin
76
+ Date.parse(date)
77
+ rescue ArgumentError
78
+ raise GiftCardDateFormatError
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
@@ -0,0 +1 @@
1
+ Spree::OrderContents.include Spree::GiftCards::OrderContentsConcerns
@@ -5,22 +5,25 @@ class Spree::VirtualGiftCard < ActiveRecord::Base
5
5
  belongs_to :purchaser, class_name: 'Spree::User'
6
6
  belongs_to :redeemer, class_name: 'Spree::User'
7
7
  belongs_to :line_item, class_name: 'Spree::LineItem'
8
- before_create :set_redemption_code, unless: -> { redemption_code }
9
-
10
8
 
11
9
  validates :amount, numericality: { greater_than: 0 }
12
- validates_uniqueness_of :redemption_code, conditions: -> { where(redeemed_at: nil) }
13
- validates_presence_of :purchaser_id
10
+ validates_uniqueness_of :redemption_code, conditions: -> { where(redeemed_at: nil, redeemable: true) }
11
+ validates_presence_of :purchaser_id, if: Proc.new { |gc| gc.redeemable? }
14
12
 
15
13
  scope :unredeemed, -> { where(redeemed_at: nil) }
16
14
  scope :by_redemption_code, -> (redemption_code) { where(redemption_code: redemption_code) }
15
+ scope :purchased, -> { where(redeemable: true) }
16
+
17
+ ransacker :sent_at do
18
+ Arel.sql('date(sent_at)')
19
+ end
17
20
 
18
21
  def redeemed?
19
22
  redeemed_at.present?
20
23
  end
21
24
 
22
25
  def redeem(redeemer)
23
- return false if redeemed?
26
+ return false if redeemed? || !redeemable?
24
27
  create_store_credit!({
25
28
  amount: amount,
26
29
  currency: currency,
@@ -33,18 +36,51 @@ class Spree::VirtualGiftCard < ActiveRecord::Base
33
36
  self.update_attributes( redeemed_at: Time.now, redeemer: redeemer )
34
37
  end
35
38
 
39
+ def make_redeemable!(purchaser:)
40
+ update_attributes!(redeemable: true, purchaser: purchaser, redemption_code: (self.redemption_code || generate_unique_redemption_code))
41
+ end
42
+
36
43
  def memo
37
44
  "Gift Card ##{self.redemption_code}"
38
45
  end
39
46
 
47
+ def details
48
+ {
49
+ amount: formatted_amount,
50
+ redemption_code: formatted_redemption_code,
51
+ recipient_email: recipient_email,
52
+ recipient_name: recipient_name,
53
+ purchaser_name: purchaser_name,
54
+ gift_message: gift_message,
55
+ send_email_at: send_email_at,
56
+ formatted_send_email_at: formatted_send_email_at
57
+ }
58
+ end
59
+
40
60
  def formatted_redemption_code
41
- redemption_code.scan(/.{4}/).join('-')
61
+ redemption_code.present? ? redemption_code.scan(/.{4}/).join('-') : ""
42
62
  end
43
63
 
44
64
  def formatted_amount
45
65
  number_to_currency(amount, precision: 0)
46
66
  end
47
67
 
68
+ def formatted_send_email_at
69
+ send_email_at.strftime("%-m/%-d/%y") if send_email_at
70
+ end
71
+
72
+ def formatted_sent_at
73
+ sent_at.strftime("%-m/%-d/%y") if sent_at
74
+ end
75
+
76
+ def formatted_created_at
77
+ created_at.localtime.strftime("%F %I:%M%p")
78
+ end
79
+
80
+ def formatted_redeemed_at
81
+ redeemed_at.localtime.strftime("%F %I:%M%p") if redeemed_at
82
+ end
83
+
48
84
  def store_credit_category
49
85
  Spree::StoreCreditCategory.where(name: Spree::StoreCreditCategory::GIFT_CARD_CATEGORY_NAME).first
50
86
  end
@@ -53,12 +89,13 @@ class Spree::VirtualGiftCard < ActiveRecord::Base
53
89
  Spree::VirtualGiftCard.unredeemed.by_redemption_code(redemption_code).first
54
90
  end
55
91
 
56
- private
57
-
58
- def set_redemption_code
59
- self.redemption_code = generate_unique_redemption_code
92
+ def send_email
93
+ Spree::GiftCardMailer.gift_card_email(self).deliver_later
94
+ update_attributes!(sent_at: DateTime.now)
60
95
  end
61
96
 
97
+ private
98
+
62
99
  def generate_unique_redemption_code
63
100
  redemption_code = Spree::RedemptionCodeGenerator.generate_redemption_code
64
101
 
@@ -0,0 +1,6 @@
1
+ Deface::Override.new(
2
+ virtual_path: "spree/admin/orders/confirm",
3
+ name: "add_gift_cards_to_admin_order_confirmation",
4
+ insert_before: "#order-total",
5
+ partial: "spree/admin/orders/confirmation_gift_card_details",
6
+ )
@@ -1,13 +1,20 @@
1
+ Deface::Override.new(
2
+ virtual_path: "spree/admin/orders/_line_items",
3
+ name: "add_gift_cards_to_admin_line_items",
4
+ insert_bottom: ".line-item-name",
5
+ partial: "spree/admin/orders/cart_gift_card_details",
6
+ )
7
+
1
8
  Deface::Override.new(
2
9
  virtual_path: "spree/admin/orders/_shipment_manifest",
3
10
  name: "admin_item_view_gift_card_codes",
4
11
  insert_bottom: ".item-name",
5
- partial: "spree/admin/orders/gift_card_redemption_codes",
12
+ partial: "spree/admin/orders/shipments_gift_card_details",
6
13
  )
7
14
 
8
15
  Deface::Override.new(
9
16
  virtual_path: "spree/admin/orders/_carton_manifest",
10
17
  name: "admin_item_view_gift_card_codes",
11
18
  insert_bottom: ".item-name",
12
- partial: "spree/admin/orders/gift_card_redemption_codes",
19
+ partial: "spree/admin/orders/shipments_gift_card_details",
13
20
  )
@@ -1,8 +1,33 @@
1
- <script async='true'>GiftCards.init()</script>
2
-
3
1
  <div class='gift-card-code-lookup'>
4
- <%= form_for :gift_card, url: admin_gift_cards_path, html: {id: 'lookup-redemption-code'} do |f| %>
5
- Redemption code: <%= f.text_field :redemption_code %><br />
6
- <%= f.submit 'Lookup Gift Card', class: 'lookup-gift-card-button' %>
2
+ <%= search_form_for @search, url: admin_gift_cards_path(@search) do |f| %>
3
+ <div class="four columns">
4
+ <div class="field">
5
+ <%= label_tag nil, "Redemption Code" %>
6
+ <%= f.text_field :redemption_code_cont, :size => 27 %>
7
+ </div>
8
+ </div>
9
+ <div class="four columns">
10
+ <div class="field">
11
+ <%= label_tag nil, "Recipient Email" %>
12
+ <%= f.text_field :recipient_email_cont, :size => 25 %>
13
+ </div>
14
+ </div>
15
+ <div class="four columns">
16
+ <div class="field">
17
+ <%= label_tag nil, "Order Number" %>
18
+ <%= f.text_field :line_item_order_number_cont, :size => 25 %>
19
+ </div>
20
+ </div>
21
+ <div class="three columns">
22
+ <div class="field">
23
+ <%= label_tag nil, "Send Date" %>
24
+ <%= f.text_field :sent_at_or_send_email_at_is, :class => 'datepicker', :size => 25 %>
25
+ </div>
26
+ </div>
27
+ <div class="clearfix"></div>
28
+ <div class="actions filter-actions">
29
+ <%= button 'Lookup Gift Card', 'search', class: 'lookup-gift-card-button' %>
30
+ </div>
7
31
  <% end %>
8
- </div>
32
+ </div>
33
+