workarea-gift_cards 3.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.eslintignore +2 -0
  4. data/.eslintrc +24 -0
  5. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  6. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. data/.gitignore +15 -0
  9. data/.scss-lint.yml +188 -0
  10. data/.yardopts +1 -0
  11. data/CHANGELOG.md +485 -0
  12. data/CODE_OF_CONDUCT.md +3 -0
  13. data/CONTRIBUTING.md +3 -0
  14. data/Gemfile +7 -0
  15. data/LICENSE +52 -0
  16. data/README.md +119 -0
  17. data/Rakefile +53 -0
  18. data/app/assets/images/workarea/admin/payment_icons/gift_card.svg +1 -0
  19. data/app/assets/images/workarea/storefront/payment_icons/gift_card.svg +1 -0
  20. data/app/controllers/workarea/admin/data_file_exports_controller.decorator +28 -0
  21. data/app/controllers/workarea/admin/payment_gift_cards_controller.rb +78 -0
  22. data/app/controllers/workarea/api/admin/payment_gift_cards_controller.rb +49 -0
  23. data/app/controllers/workarea/api/storefront/checkouts_controller.decorator +10 -0
  24. data/app/controllers/workarea/api/storefront/gift_cards_controller.rb +26 -0
  25. data/app/controllers/workarea/storefront/checkouts_controller.decorator +24 -0
  26. data/app/controllers/workarea/storefront/gift_cards_controller.rb +24 -0
  27. data/app/mailers/workarea/storefront/gift_card_mailer.rb +28 -0
  28. data/app/models/workarea/catalog/customizations/gift_card.rb +9 -0
  29. data/app/models/workarea/catalog/product.decorator +11 -0
  30. data/app/models/workarea/order/item.decorator +7 -0
  31. data/app/models/workarea/payment/authorize/gift_card.rb +43 -0
  32. data/app/models/workarea/payment/capture/gift_card.rb +25 -0
  33. data/app/models/workarea/payment/gift_card/redemption.rb +23 -0
  34. data/app/models/workarea/payment/gift_card.rb +186 -0
  35. data/app/models/workarea/payment/purchase/gift_card.rb +7 -0
  36. data/app/models/workarea/payment/refund/gift_card.rb +35 -0
  37. data/app/models/workarea/payment/tender/gift_card.rb +30 -0
  38. data/app/models/workarea/payment.decorator +19 -0
  39. data/app/models/workarea/search/admin/order.decorator +9 -0
  40. data/app/models/workarea/search/admin/payment_gift_card.rb +46 -0
  41. data/app/queries/workarea/admin_redemptions_export.rb +84 -0
  42. data/app/queries/workarea/order_item_details.decorator +11 -0
  43. data/app/queries/workarea/search/admin_gift_cards.rb +20 -0
  44. data/app/seeds/workarea/gift_card_seeds.rb +44 -0
  45. data/app/services/workarea/checkout/steps/gift_card.rb +49 -0
  46. data/app/view_models/workarea/admin/payment_gift_card_view_model.rb +11 -0
  47. data/app/view_models/workarea/storefront/checkout/payment_view_model.decorator +32 -0
  48. data/app/view_models/workarea/storefront/checkout/summary_view_model.decorator +11 -0
  49. data/app/view_models/workarea/storefront/gift_card_order_pricing.rb +36 -0
  50. data/app/view_models/workarea/storefront/order_view_model.decorator +27 -0
  51. data/app/view_models/workarea/storefront/product_templates/gift_card_view_model.rb +4 -0
  52. data/app/views/workarea/admin/activities/_payment_gift_card_create.html.haml +12 -0
  53. data/app/views/workarea/admin/activities/_payment_gift_card_destroy.html.haml +10 -0
  54. data/app/views/workarea/admin/activities/_payment_gift_card_update.html.haml +14 -0
  55. data/app/views/workarea/admin/orders/tenders/_gift_card.html.haml +5 -0
  56. data/app/views/workarea/admin/payment_gift_cards/_aux_navigation.html.haml +4 -0
  57. data/app/views/workarea/admin/payment_gift_cards/_cards.html.haml +74 -0
  58. data/app/views/workarea/admin/payment_gift_cards/_menu.html.haml +1 -0
  59. data/app/views/workarea/admin/payment_gift_cards/_summary.html.haml +11 -0
  60. data/app/views/workarea/admin/payment_gift_cards/edit.html.haml +37 -0
  61. data/app/views/workarea/admin/payment_gift_cards/index.html.haml +95 -0
  62. data/app/views/workarea/admin/payment_gift_cards/new.html.haml +43 -0
  63. data/app/views/workarea/admin/payment_gift_cards/redemptions.html.haml +29 -0
  64. data/app/views/workarea/admin/payment_gift_cards/show.html.haml +19 -0
  65. data/app/views/workarea/api/storefront/checkouts/steps/_gift_card.json.jbuilder +6 -0
  66. data/app/views/workarea/api/storefront/gift_cards/balance.json.jbuilder +3 -0
  67. data/app/views/workarea/api/storefront/orders/tenders/_gift_card.json.jbuilder +3 -0
  68. data/app/views/workarea/storefront/checkouts/_gift_card_error.html.haml +6 -0
  69. data/app/views/workarea/storefront/checkouts/_gift_card_payment.html.haml +28 -0
  70. data/app/views/workarea/storefront/checkouts/_gift_card_summary.html.haml +6 -0
  71. data/app/views/workarea/storefront/gift_card_mailer/created.html.haml +15 -0
  72. data/app/views/workarea/storefront/gift_cards/index.html.haml +17 -0
  73. data/app/views/workarea/storefront/order_mailer/_gift_card_summary.html.haml +5 -0
  74. data/app/views/workarea/storefront/order_mailer/tenders/_gift_card.html.haml +5 -0
  75. data/app/views/workarea/storefront/orders/_gift_card_summary.html.haml +6 -0
  76. data/app/views/workarea/storefront/orders/tenders/_gift_card.html.haml +9 -0
  77. data/app/views/workarea/storefront/products/templates/_gift_card.html.haml +77 -0
  78. data/app/workers/workarea/create_ordered_gift_cards.rb +47 -0
  79. data/app/workers/workarea/log_gift_card_redemption.rb +32 -0
  80. data/app/workers/workarea/send_gift_card_notifications.rb +20 -0
  81. data/bin/rails +18 -0
  82. data/config/initializers/append_points.rb +37 -0
  83. data/config/initializers/configuration.rb +8 -0
  84. data/config/initializers/jump_to_navigation.rb +3 -0
  85. data/config/locales/en.yml +82 -0
  86. data/config/routes.rb +31 -0
  87. data/lib/workarea/gift_cards/engine.rb +19 -0
  88. data/lib/workarea/gift_cards/version.rb +5 -0
  89. data/lib/workarea/gift_cards.rb +11 -0
  90. data/lib/workarea/mailer_previews/storefront/gift_card_mailer_preview.rb +18 -0
  91. data/test/documentation/workarea/api/admin/gift_cards_documentation_test.rb +82 -0
  92. data/test/documentation/workarea/api/storefront/gift_cards_documentation_test.rb +107 -0
  93. data/test/dummy/Rakefile +6 -0
  94. data/test/dummy/app/assets/config/manifest.js +4 -0
  95. data/test/dummy/app/assets/images/.keep +0 -0
  96. data/test/dummy/app/assets/javascripts/application.js +13 -0
  97. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  98. data/test/dummy/app/controllers/application_controller.rb +3 -0
  99. data/test/dummy/app/controllers/concerns/.keep +0 -0
  100. data/test/dummy/app/helpers/application_helper.rb +2 -0
  101. data/test/dummy/app/jobs/application_job.rb +2 -0
  102. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  103. data/test/dummy/app/models/concerns/.keep +0 -0
  104. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  105. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  106. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  107. data/test/dummy/bin/bundle +3 -0
  108. data/test/dummy/bin/rails +4 -0
  109. data/test/dummy/bin/rake +4 -0
  110. data/test/dummy/bin/setup +34 -0
  111. data/test/dummy/bin/update +29 -0
  112. data/test/dummy/config/application.rb +24 -0
  113. data/test/dummy/config/boot.rb +5 -0
  114. data/test/dummy/config/cable.yml +9 -0
  115. data/test/dummy/config/environment.rb +5 -0
  116. data/test/dummy/config/environments/development.rb +54 -0
  117. data/test/dummy/config/environments/production.rb +86 -0
  118. data/test/dummy/config/environments/test.rb +43 -0
  119. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  120. data/test/dummy/config/initializers/assets.rb +11 -0
  121. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  122. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  123. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  124. data/test/dummy/config/initializers/inflections.rb +16 -0
  125. data/test/dummy/config/initializers/mime_types.rb +4 -0
  126. data/test/dummy/config/initializers/new_framework_defaults.rb +21 -0
  127. data/test/dummy/config/initializers/session_store.rb +3 -0
  128. data/test/dummy/config/initializers/workarea.rb +5 -0
  129. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  130. data/test/dummy/config/locales/en.yml +23 -0
  131. data/test/dummy/config/puma.rb +47 -0
  132. data/test/dummy/config/routes.rb +6 -0
  133. data/test/dummy/config/secrets.yml +22 -0
  134. data/test/dummy/config/spring.rb +6 -0
  135. data/test/dummy/config.ru +5 -0
  136. data/test/dummy/lib/assets/.keep +0 -0
  137. data/test/dummy/log/.keep +0 -0
  138. data/test/dummy/public/404.html +67 -0
  139. data/test/dummy/public/422.html +67 -0
  140. data/test/dummy/public/500.html +66 -0
  141. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  142. data/test/dummy/public/apple-touch-icon.png +0 -0
  143. data/test/dummy/public/favicon.ico +0 -0
  144. data/test/factories/gift_cards.rb +27 -0
  145. data/test/integration/workarea/admin/gift_card_integration_test.rb +53 -0
  146. data/test/integration/workarea/admin/gift_card_redemptions_export_integration_test.rb +104 -0
  147. data/test/integration/workarea/api/admin/gift_card_integration_test.rb +57 -0
  148. data/test/integration/workarea/api/storefront/balance_integration_test.rb +40 -0
  149. data/test/integration/workarea/api/storefront/checkout_integration_test.rb +161 -0
  150. data/test/integration/workarea/storefront/gift_card_integration_test.rb +222 -0
  151. data/test/models/workarea/catalog/gift_card_product_test.rb +15 -0
  152. data/test/models/workarea/gift_card_payment_test.rb +12 -0
  153. data/test/models/workarea/payment/gift_card_test.rb +127 -0
  154. data/test/models/workarea/payment/purchase/gift_card_test.rb +52 -0
  155. data/test/models/workarea/payment/refund/gift_card_test.rb +19 -0
  156. data/test/queries/workarea/admin_redemptions_export_test.rb +52 -0
  157. data/test/queries/workarea/gift_card_order_item_details_test.rb +11 -0
  158. data/test/services/workarea/checkout/steps/gift_card_test.rb +87 -0
  159. data/test/system/workarea/admin/gift_cards_system_test.rb +89 -0
  160. data/test/system/workarea/storefront/gift_cards_system_test.rb +400 -0
  161. data/test/test_helper.rb +10 -0
  162. data/test/view_models/workarea/storefront/checkout/gift_card_payment_view_model_test.rb +65 -0
  163. data/test/workers/workarea/create_ordered_gift_cards_test.rb +39 -0
  164. data/test/workers/workarea/log_gift_card_redemption_test.rb +19 -0
  165. data/workarea-gift_cards.gemspec +21 -0
  166. metadata +227 -0
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ require 'rdoc/task'
9
+
10
+ RDoc::Task.new(:rdoc) do |rdoc|
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = 'Tasker'
13
+ rdoc.options << '--line-numbers'
14
+ rdoc.rdoc_files.include('README.md')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
18
+ APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
19
+ load 'rails/tasks/engine.rake'
20
+ load 'rails/tasks/statistics.rake'
21
+ load 'workarea/changelog.rake'
22
+
23
+ require 'rake/testtask'
24
+
25
+ Rake::TestTask.new(:test) do |t|
26
+ t.libs << 'lib'
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
33
+
34
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
35
+ require 'workarea/gift_cards/version'
36
+
37
+ desc "Release version #{Workarea::GiftCards::VERSION} of the gem"
38
+ task :release do
39
+ host = "https://#{ENV['BUNDLE_GEMS__WEBLINC__COM']}@gems.weblinc.com"
40
+
41
+ #Rake::Task['workarea:changelog'].execute
42
+ #system 'git add CHANGELOG.md'
43
+ #system 'git commit -m "Update CHANGELOG"'
44
+ #system 'git push origin HEAD'
45
+
46
+ system "git tag -a v#{Workarea::GiftCards::VERSION} -m 'Tagging #{Workarea::GiftCards::VERSION}'"
47
+ system 'git push --tags'
48
+
49
+ system 'gem build workarea-gift_cards.gemspec'
50
+ system "gem push workarea-gift_cards-#{Workarea::GiftCards::VERSION}.gem"
51
+ system "gem push workarea-gift_cards-#{Workarea::GiftCards::VERSION}.gem --host #{host}"
52
+ system "rm workarea-gift_cards-#{Workarea::GiftCards::VERSION}.gem"
53
+ end
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-243 353.9 125 85.3" enable-background="new -243 353.9 125 85.3"><path d="m-229.7 402h-13.3v-37.6c0-5.8 4.7-10.5 10.5-10.5h24v34c-.2-.2-.3-.4-.5-.6-6.4-6.5-15.6-7.7-20.6-2.7-4.3 4.1-4.1 11.3-.1 17.4m9.8 29.9l-10.9-11.5 5.1-4.6h-17.4v12.9c0 5.8 4.7 10.5 10.5 10.5h24v-17.9l-11.3 10.6zm26.3-44.6c6.4-6.5 15.6-7.7 20.6-2.7 4.2 4.2 4.1 11.4.1 17.4h54.9v-37.6c0-5.8-4.7-10.5-10.5-10.5h-65.6v34c.2-.3.3-.4.5-.6m21.7 33.7l-11.5 10.9-10.7-11.3v18.6h65.6c5.8 0 10.5-4.7 10.5-10.5v-12.9h-59.1l5.2 5.2zm-18.5-28.1c-3.3 3.3-3.9 13.2-3.9 13.2s9.9-.7 13.2-4c3.3-3.3 3.9-8 1.3-10.6-2.6-2.5-7.3-1.9-10.6 1.4m-32.4-1.4c-2.6 2.5-2 7.3 1.3 10.6 3.3 3.3 13.2 4 13.2 4s-.6-9.9-3.9-13.2c-3.3-3.3-8-3.9-10.6-1.4"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-243 353.9 125 85.3" enable-background="new -243 353.9 125 85.3"><path d="m-229.7 402h-13.3v-37.6c0-5.8 4.7-10.5 10.5-10.5h24v34c-.2-.2-.3-.4-.5-.6-6.4-6.5-15.6-7.7-20.6-2.7-4.3 4.1-4.1 11.3-.1 17.4m9.8 29.9l-10.9-11.5 5.1-4.6h-17.4v12.9c0 5.8 4.7 10.5 10.5 10.5h24v-17.9l-11.3 10.6zm26.3-44.6c6.4-6.5 15.6-7.7 20.6-2.7 4.2 4.2 4.1 11.4.1 17.4h54.9v-37.6c0-5.8-4.7-10.5-10.5-10.5h-65.6v34c.2-.3.3-.4.5-.6m21.7 33.7l-11.5 10.9-10.7-11.3v18.6h65.6c5.8 0 10.5-4.7 10.5-10.5v-12.9h-59.1l5.2 5.2zm-18.5-28.1c-3.3 3.3-3.9 13.2-3.9 13.2s9.9-.7 13.2-4c3.3-3.3 3.9-8 1.3-10.6-2.6-2.5-7.3-1.9-10.6 1.4m-32.4-1.4c-2.6 2.5-2 7.3 1.3 10.6 3.3 3.3 13.2 4 13.2 4s-.6-9.9-3.9-13.2c-3.3-3.3-8-3.9-10.6-1.4"/></svg>
@@ -0,0 +1,28 @@
1
+ module Workarea
2
+ decorate Admin::DataFileExportsController, with: 'gift_cards' do
3
+ def set_export
4
+ super
5
+
6
+ if @export.model_class == Workarea::Payment::GiftCard::Redemption
7
+ convert_export_ids_to_query if @export.ids.present?
8
+ convert_query_to_redemptions_query
9
+ end
10
+ end
11
+
12
+ def convert_export_ids_to_query
13
+ tokens = GlobalID::Locator.locate_many(@export.ids).map(&:token)
14
+ @export.query_id = Search::AdminGiftCards.new(q: tokens.join(' ')).to_gid_param
15
+ @export.ids = []
16
+ end
17
+
18
+ def convert_query_to_redemptions_query
19
+ current_query = GlobalID.find(@export.query_id)
20
+
21
+ unless current_query.blank? || current_query.is_a?(AdminRedemptionsExport)
22
+ @export.query_id = AdminRedemptionsExport
23
+ .new(gift_cards_query_id: @export.query_id)
24
+ .to_gid_param
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,78 @@
1
+ module Workarea
2
+ module Admin
3
+ class PaymentGiftCardsController < Admin::ApplicationController
4
+ required_permissions :orders
5
+ before_action :find_gift_card, except: :index
6
+
7
+ def index
8
+ search = Search::AdminGiftCards.new(params)
9
+ @search = Admin::SearchViewModel.new(search, view_model_options)
10
+ end
11
+
12
+ def show; end
13
+
14
+ def new; end
15
+
16
+ def create
17
+ if @gift_card.save
18
+ flash[:success] = 'Gift card has been created.'
19
+ redirect_to payment_gift_card_path(@gift_card)
20
+ else
21
+ render :new
22
+ end
23
+ end
24
+
25
+ def edit; end
26
+
27
+ def update
28
+ if @gift_card.update_attributes(gift_card_params)
29
+ flash[:success] = 'Gift card has been saved.'
30
+ redirect_to payment_gift_card_path(@gift_card)
31
+ else
32
+ render :edit
33
+ end
34
+ end
35
+
36
+ def destroy
37
+ @gift_card.destroy
38
+ flash[:success] = "Gift card #{@gift_card.token} has been removed."
39
+ redirect_to payment_gift_cards_path
40
+ end
41
+
42
+ def redemptions
43
+ end
44
+
45
+ private
46
+
47
+ def find_gift_card
48
+ gift_card =
49
+ if params[:id].present?
50
+ Payment::GiftCard.find(params[:id])
51
+ elsif params[:payment_gift_card_id].present?
52
+ Payment::GiftCard.find(params[:payment_gift_card_id])
53
+ else
54
+ Payment::GiftCard.new(gift_card_params)
55
+ end
56
+
57
+ @gift_card =
58
+ Admin::PaymentGiftCardViewModel.new(
59
+ gift_card,
60
+ view_model_options
61
+ )
62
+ end
63
+
64
+ def gift_card_params
65
+ return {} if params[:gift_card].blank?
66
+ params[:gift_card].permit(
67
+ :token,
68
+ :to,
69
+ :from,
70
+ :message,
71
+ :expires_at,
72
+ :amount,
73
+ :notify
74
+ )
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,49 @@
1
+ module Workarea
2
+ if Plugin.installed?(:api)
3
+ module Api
4
+ module Admin
5
+ class PaymentGiftCardsController < Admin::ApplicationController
6
+ before_action :find_gift_card, except: [:index, :create]
7
+
8
+ def index
9
+ @gift_cards = Payment::GiftCard
10
+ .all
11
+ .order_by(sort_field => sort_direction)
12
+ .page(params[:page])
13
+
14
+ respond_with gift_cards: @gift_cards
15
+ end
16
+
17
+ def show
18
+ respond_with gift_card: @gift_card
19
+ end
20
+
21
+ def update
22
+ @gift_card.update_attributes!(params[:gift_card])
23
+ respond_with gift_card: @gift_card
24
+ end
25
+
26
+ def create
27
+ @gift_card = Payment::GiftCard.create!(params[:gift_card])
28
+ respond_with(
29
+ { gift_card: @gift_card },
30
+ { status: :created,
31
+ location: payment_gift_card_path(@gift_card) }
32
+ )
33
+ end
34
+
35
+ def destroy
36
+ @gift_card.destroy
37
+ head :no_content
38
+ end
39
+
40
+ private
41
+
42
+ def find_gift_card
43
+ @gift_card = Payment::GiftCard.find(params[:id])
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,10 @@
1
+ module Workarea
2
+ if Plugin.installed?(:api)
3
+ decorate Api::Storefront::CheckoutsController, with: 'gift_cards' do
4
+ def add_gift_card
5
+ Checkout::Steps::GiftCard.new(current_checkout).update(params)
6
+ render_checkout
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ module Workarea
2
+ if Plugin.installed?(:api)
3
+ module Api
4
+ module Storefront
5
+ class GiftCardsController < Api::Storefront::ApplicationController
6
+ def balance
7
+ email = params.fetch(:email, nil)
8
+ token = params.fetch(:token, nil)
9
+
10
+ if email.present? && token.present?
11
+ @gift_card = Payment::GiftCard.find_by_token_and_email(token, email)
12
+ end
13
+
14
+ if @gift_card.blank?
15
+ raise Mongoid::Errors::DocumentNotFound.new(
16
+ Payment::GiftCard,
17
+ email: email,
18
+ token: token
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ module Workarea
2
+ decorate Storefront::CheckoutsController, with: 'gift_cards' do
3
+ def add_gift_card
4
+ step = Checkout::Steps::GiftCard.new(current_checkout)
5
+
6
+ if step.update(params)
7
+ flash[:success] = 'Your gift card has been applied.'
8
+ redirect_to checkout_payment_path
9
+ else
10
+ flash[:error] = <<-string
11
+ We couldn't add this gift card number.
12
+ Please ensure you have entered the correct number.
13
+ string
14
+
15
+ @step = Storefront::Checkout::PaymentViewModel.new(
16
+ step,
17
+ view_model_options
18
+ )
19
+
20
+ render :payment
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Workarea
2
+ class Storefront::GiftCardsController < Storefront::ApplicationController
3
+ def index
4
+ end
5
+
6
+ def lookup
7
+ email = params.fetch(:email, nil)
8
+ token = params.fetch(:token, nil)
9
+
10
+ if email.present? && token.present?
11
+ @gift_card = Workarea::Payment::GiftCard
12
+ .find_by_token_and_email(token, email)
13
+ end
14
+
15
+ if @gift_card.present?
16
+ flash[:success] = "Your balance is #{@gift_card.balance.format}"
17
+ else
18
+ flash[:error] = 'Invalid card number or email.'
19
+ end
20
+
21
+ redirect_to gift_cards_balance_path
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ module Workarea
2
+ module Storefront
3
+ class GiftCardMailer < Storefront::ApplicationMailer
4
+ def created(card_id)
5
+ @card = Payment::GiftCard.find(card_id)
6
+ @from = from(@card.from, @card.order_id)
7
+
8
+ mail(
9
+ to: @card.to,
10
+ from: Workarea.config.email_from,
11
+ subject: t('workarea.storefront.email.gift_card_created.subject')
12
+ )
13
+ end
14
+
15
+ private
16
+
17
+ def from(card_from, order_id)
18
+ card_from || card_order_email(order_id) || Workarea.config.email_from
19
+ end
20
+
21
+ def card_order_email(order_id)
22
+ return unless order_id.present?
23
+ order = Order.find(order_id)
24
+ order.email
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ class Workarea::Catalog::Customizations::GiftCard < Workarea::Catalog::Customizations
2
+ customized_fields :email, :from, :message
3
+
4
+ validates :email, presence: true,
5
+ email: true
6
+
7
+ validates :from, length: { maximum: 100 }
8
+ validates :message, length: { maximum: 500 }
9
+ end
@@ -0,0 +1,11 @@
1
+ module Workarea
2
+ decorate Catalog::Product, with: 'gift_cards' do
3
+ decorated do
4
+ field :gift_card, type: Boolean, default: false
5
+ end
6
+
7
+ def gift_card?
8
+ super || template == 'gift_card'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Order::Item, with: 'gift_cards' do
3
+ decorated do
4
+ field :gift_card, type: Boolean, default: false
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,43 @@
1
+ module Workarea
2
+ class Payment
3
+ module Authorize
4
+ class GiftCard
5
+ include OperationImplementation
6
+
7
+ def complete!
8
+ Payment::GiftCard.purchase(tender.number, transaction.amount.cents)
9
+
10
+ transaction.response = ActiveMerchant::Billing::Response.new(
11
+ true,
12
+ I18n.t(
13
+ 'workarea.gift_cards.debit',
14
+ amount: transaction.amount,
15
+ number: tender.number
16
+ )
17
+ )
18
+
19
+ rescue Payment::InsufficientFunds
20
+ transaction.response = ActiveMerchant::Billing::Response.new(
21
+ false,
22
+ I18n.t('workarea.gift_cards.insufficient_funds')
23
+ )
24
+ end
25
+
26
+ def cancel!
27
+ return unless transaction.success?
28
+
29
+ Payment::GiftCard.refund(tender.number, transaction.amount.cents)
30
+
31
+ transaction.cancellation = ActiveMerchant::Billing::Response.new(
32
+ true,
33
+ I18n.t(
34
+ 'workarea.gift_cards.credit',
35
+ amount: transaction.amount,
36
+ number: tender.number
37
+ )
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ module Workarea
2
+ class Payment
3
+ class Capture
4
+ class GiftCard
5
+ include OperationImplementation
6
+
7
+ def complete!
8
+ # noop, authorization does the capture
9
+ transaction.response = ActiveMerchant::Billing::Response.new(
10
+ true,
11
+ I18n.t('workarea.gift_cards.capture')
12
+ )
13
+ end
14
+
15
+ def cancel!
16
+ # noop, nothing to cancel
17
+ transaction.response = ActiveMerchant::Billing::Response.new(
18
+ true,
19
+ I18n.t('workarea.gift_cards.capture')
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module Workarea
2
+ class Payment
3
+ class GiftCard
4
+ class Redemption
5
+ include ApplicationDocument
6
+
7
+ field :redeemed_at, type: DateTime
8
+ field :order_id, type: String
9
+
10
+ field :amount, type: Money
11
+
12
+ belongs_to :gift_card, class_name: 'Workarea::Payment::GiftCard'
13
+
14
+ validates_presence_of :amount
15
+ validates_presence_of :redeemed_at
16
+
17
+ index(redeemed_at: 1)
18
+ index(order_id: 1)
19
+ index(gift_card_id: 1)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,186 @@
1
+ module Workarea
2
+ class Payment
3
+ class GiftCard
4
+ include ApplicationDocument
5
+ include Commentable
6
+
7
+ field :token, type: String
8
+ field :to, type: String
9
+ field :from, type: String
10
+ field :message, type: String
11
+ field :expires_at, type: DateTime
12
+ field :notify, type: Boolean
13
+ field :purchased, type: Boolean
14
+
15
+ # Order id if this card was created
16
+ # by a customer purchasing it.
17
+ field :order_id, type: String
18
+
19
+ field :amount, type: Money, default: 0.to_m
20
+ field :used, type: Money, default: 0.to_m
21
+ field :balance, type: Money, default: 0.to_m
22
+
23
+ has_many :redemptions, class_name: 'Workarea::Payment::GiftCard::Redemption'
24
+
25
+ index({ token: 1 }, { unique: true })
26
+ index(to: 1)
27
+ index('balance.cents' => 1)
28
+ index(expires_at: 1)
29
+ index(created_at: 1)
30
+
31
+ alias number token
32
+
33
+ validates :token, presence: true
34
+ validates :amount, presence: true
35
+ validates :to, email: true
36
+
37
+ scope :not_expired, -> {
38
+ any_of({ :expires_at.exists => false },
39
+ { expires_at: nil },
40
+ { :expires_at.gte => Time.now })
41
+ }
42
+
43
+ before_validation :assign_token
44
+
45
+ # Sorts available for this class.
46
+ #
47
+ # @return [Array<Workarea::Sort>]
48
+ #
49
+ def self.sorts
50
+ [Workarea::Sort.newest, Workarea::Sort.modified]
51
+ end
52
+
53
+ # Find gift cards whose token or recipient matches the
54
+ # given string. Used in admin filtering.
55
+ #
56
+ # @param [String]
57
+ # @return [Mongoid::Criteria]
58
+ #
59
+ def self.search_by_token_or_recipient(string)
60
+ regex = /^#{::Regexp.quote(string)}/i
61
+ any_of({ token: regex }, { to: regex })
62
+ end
63
+
64
+ # Performs purchase of the gift card that matches the given token.
65
+ # Purchase is for the amount in cents.
66
+ #
67
+ # @param [String] token Representing Payment::GiftCard#token
68
+ # @param [Integer] cents Amount of purchase
69
+ # @return [Payment::GiftCard]
70
+ #
71
+ def self.purchase(token, cents)
72
+ not_expired.find_by_token(token).purchase(cents)
73
+ end
74
+
75
+ # Performs refund of the gift card that matches the given token.
76
+ # Refund is for the amount in cents.
77
+ #
78
+ # @param [String] token Representing Payment::GiftCard#token
79
+ # @param [Integer] cents Amount to refund
80
+ # @return [Payment::GiftCard]
81
+ #
82
+ def self.refund(token, cents)
83
+ find_by_token(token).refund(cents)
84
+ end
85
+
86
+ # Gift card balance for the given token. If the Gift card does
87
+ # not exist, $0 is returned.
88
+ #
89
+ # @param [String] token Representing Payment::GiftCard#token
90
+ # @return [Money]
91
+ #
92
+ def self.find_balance(token)
93
+ card = not_expired.find_by_token(token)
94
+ card ? card.balance : 0.to_m
95
+ end
96
+
97
+ def self.find_by_token(token)
98
+ where(token: token.downcase).first
99
+ end
100
+
101
+ def self.find_by_token_and_email(token, email)
102
+ where(token: token, to: email.downcase).not_expired.first
103
+ end
104
+
105
+ def to=(val)
106
+ if val.nil?
107
+ super(val)
108
+ else
109
+ super(val.to_s.downcase)
110
+ end
111
+ end
112
+
113
+ def valid?(*)
114
+ super.tap do |result|
115
+ self.balance = amount if result && !persisted?
116
+ self.token = token.downcase if token.present?
117
+ end
118
+ end
119
+
120
+ # This is for compatibility with the admin, all models must implement this
121
+ #
122
+ # @return [String]
123
+ #
124
+ def name
125
+ I18n.t('workarea.payment_gift_card.name', token: token)
126
+ end
127
+
128
+ # Whether the gift card has expired.
129
+ #
130
+ # @return [Boolean]
131
+ #
132
+ def expired?
133
+ !!(expires_at && Time.now >= expires_at)
134
+ end
135
+
136
+ # The date of the last redemption.
137
+ #
138
+ # @return [DateTime]
139
+ #
140
+ def last_redeemed_at
141
+ redemptions.asc(:redeemed_at).last.try(:redeemed_at)
142
+ end
143
+
144
+ # Performs purchase for the amount in cents.
145
+ #
146
+ # @param [Integer] cents Amount of purchase
147
+ # @return [Payment::GiftCard]
148
+ #
149
+ # @raise [Payment::InsufficientFunds]
150
+ # the balance of the gift card does not cover the cents
151
+ #
152
+ def purchase(cents)
153
+ raise(Workarea::Payment::InsufficientFunds) if balance.cents < cents
154
+ inc('balance.cents' => 0 - cents, 'used.cents' => cents)
155
+ self
156
+ end
157
+
158
+ # Performs refund for the amount in cents.
159
+ #
160
+ # @param [Integer] cents Amount of purchase
161
+ # @return [Payment::GiftCard]
162
+ #
163
+ def refund(cents)
164
+ inc('balance.cents' => cents, 'used.cents' => 0 - cents)
165
+ self
166
+ end
167
+
168
+ def reset_token!
169
+ update_attribute(:token, generate_token)
170
+ end
171
+
172
+ def generate_token
173
+ loop do
174
+ tmp = SecureRandom.hex(Workarea.config.gift_card_token_length / 2).downcase
175
+ break tmp unless self.class.where(token: tmp).exists?
176
+ end
177
+ end
178
+
179
+ private
180
+
181
+ def assign_token
182
+ self.token = generate_token if token.blank?
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ class Payment
3
+ module Purchase
4
+ GiftCard = Authorize::GiftCard
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ module Workarea
2
+ class Payment
3
+ class Refund
4
+ class GiftCard
5
+ include OperationImplementation
6
+
7
+ def complete!
8
+ Payment::GiftCard.refund(tender.number, transaction.amount.cents)
9
+ transaction.response = ActiveMerchant::Billing::Response.new(
10
+ true,
11
+ I18n.t(
12
+ 'workarea.gift_cards.credit',
13
+ amount: transaction.amount,
14
+ number: tender.number
15
+ )
16
+ )
17
+ end
18
+
19
+ def cancel!
20
+ return unless transaction.success?
21
+
22
+ Payment::GiftCard.purchase(tender.number, transaction.amount.cents)
23
+ transaction.cancellation = ActiveMerchant::Billing::Response.new(
24
+ true,
25
+ I18n.t(
26
+ 'workarea.gift_cards.debit',
27
+ amount: transaction.amount,
28
+ number: tender.number
29
+ )
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end