workarea-gift_cards 3.4.6

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 (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