effective_products 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +105 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/effective_products_manifest.js +3 -0
  6. data/app/assets/javascripts/effective_products/base.js +0 -0
  7. data/app/assets/javascripts/effective_products.js +1 -0
  8. data/app/assets/stylesheets/effective_products/base.scss +0 -0
  9. data/app/assets/stylesheets/effective_products.scss +1 -0
  10. data/app/controllers/admin/rings_controller.rb +9 -0
  11. data/app/controllers/effective/ring_payments_controller.rb +31 -0
  12. data/app/datatables/admin/effective_rings_datatable.rb +42 -0
  13. data/app/datatables/effective_ring_payments_datatable.rb +36 -0
  14. data/app/helpers/effective_products_helper.rb +3 -0
  15. data/app/models/concerns/effective_products_ring_payment.rb +164 -0
  16. data/app/models/effective/ring.rb +79 -0
  17. data/app/models/effective/ring_payment.rb +7 -0
  18. data/app/views/admin/rings/_ring.html.haml +40 -0
  19. data/app/views/effective/ring_payments/_content.html.haml +10 -0
  20. data/app/views/effective/ring_payments/_dashboard.html.haml +27 -0
  21. data/app/views/effective/ring_payments/_layout.html.haml +3 -0
  22. data/app/views/effective/ring_payments/_orders.html.haml +4 -0
  23. data/app/views/effective/ring_payments/_ring.html.haml +29 -0
  24. data/app/views/effective/ring_payments/_ring_fields.html.haml +4 -0
  25. data/app/views/effective/ring_payments/_ring_payment.html.haml +8 -0
  26. data/app/views/effective/ring_payments/_summary.html.haml +31 -0
  27. data/app/views/effective/ring_payments/billing.html.haml +15 -0
  28. data/app/views/effective/ring_payments/checkout.html.haml +6 -0
  29. data/app/views/effective/ring_payments/ring.html.haml +18 -0
  30. data/app/views/effective/ring_payments/start.html.haml +16 -0
  31. data/app/views/effective/ring_payments/submitted.html.haml +15 -0
  32. data/app/views/effective/ring_payments/summary.html.haml +8 -0
  33. data/app/views/effective/rings/_fields.html.haml +39 -0
  34. data/config/effective_products.rb +18 -0
  35. data/config/routes.rb +23 -0
  36. data/db/migrate/01_create_effective_products.rb.erb +4 -0
  37. data/db/seeds.rb +1 -0
  38. data/lib/effective_products/engine.rb +18 -0
  39. data/lib/effective_products/version.rb +3 -0
  40. data/lib/effective_products.rb +26 -0
  41. data/lib/generators/effective_products/install_generator.rb +30 -0
  42. data/lib/generators/templates/effective_products_mailer_preview.rb +4 -0
  43. data/lib/tasks/effective_products_tasks.rake +8 -0
  44. metadata +281 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2992b6b0b12c9492acc501695ca38ffd7a8bd12afbd3c428eaf7c2be16f62cfb
4
+ data.tar.gz: c40613e452d37c8b98617a7d13274b5c5968a7b4ea235a1581938826cfd447fa
5
+ SHA512:
6
+ metadata.gz: 8528fc8d11c0e4d71d635e6a05afb2e455425a35439637638b5d074da5538e2e0fa7e68b28e569c71c9fc918acd2665f89595b519df7a15dbd0d36a3b69dd274
7
+ data.tar.gz: 6cf09696ebbe1d0e9b534c128130647b2c73d9599d9961b082d0f7d96cc40f7a89b206ee6f8e54f969ce4678cdff3cf273ed5a380791f4733aa7bdc8e70a1987
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2022 Code and Effect Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # Effective Products
2
+
3
+ Highly customized product sales for member associations
4
+
5
+ ## Getting Started
6
+
7
+ This requires Rails 6+ and Twitter Bootstrap 4 and just works with Devise.
8
+
9
+ Please first install the [effective_datatables](https://github.com/code-and-effect/effective_datatables) gem.
10
+
11
+ Please download and install the [Twitter Bootstrap4](http://getbootstrap.com)
12
+
13
+ Add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem 'haml-rails' # or try using gem 'hamlit-rails'
17
+ gem 'effective_products'
18
+ ```
19
+
20
+ Run the bundle command to install it:
21
+
22
+ ```console
23
+ bundle install
24
+ ```
25
+
26
+ Then run the generator:
27
+
28
+ ```ruby
29
+ rails generate effective_products:install
30
+ ```
31
+
32
+ The generator will install an initializer which describes all configuration options and creates a database migration.
33
+
34
+ If you want to tweak the table names, manually adjust both the configuration file and the migration now.
35
+
36
+ Then migrate the database:
37
+
38
+ ```ruby
39
+ rake db:migrate
40
+ ```
41
+
42
+ Please add the following to your User model:
43
+
44
+ ```
45
+ ```
46
+
47
+ and
48
+
49
+ ```
50
+ Add a link to the admin menu:
51
+
52
+ ```haml
53
+ - if can? :admin, :effective_products
54
+ - if can? :index, Effective::Product
55
+ = nav_link_to 'Products', effective_products.admin_products_path
56
+ ```
57
+
58
+ ## Configuration
59
+
60
+ ## Authorization
61
+
62
+ All authorization checks are handled via the effective_resources gem found in the `config/initializers/effective_resources.rb` file.
63
+
64
+ ## Effective Roles
65
+
66
+ This gem works with effective roles for the representative roles.
67
+
68
+ Configure your `config/initializers/effective_roles.rb` something like this:
69
+
70
+ ```
71
+ ```
72
+
73
+ ## Permissions
74
+
75
+ The permissions you actually want to define are as follows (using CanCan):
76
+
77
+ ```ruby
78
+ if user.persisted?
79
+ end
80
+
81
+ if user.admin?
82
+ can :admin, :effective_products
83
+ end
84
+ ```
85
+
86
+ ## License
87
+
88
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
89
+
90
+ ## Testing
91
+
92
+ Run tests by:
93
+
94
+ ```ruby
95
+ rails test
96
+ ```
97
+
98
+ ## Contributing
99
+
100
+ 1. Fork it
101
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
102
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
103
+ 4. Push to the branch (`git push origin my-new-feature`)
104
+ 5. Bonus points for test coverage
105
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
9
+
10
+ require "rake/testtask"
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
16
+ end
17
+
18
+ task default: :test
@@ -0,0 +1,3 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
3
+ //= link_tree ../images
File without changes
@@ -0,0 +1 @@
1
+ //= require_tree ./effective_products
@@ -0,0 +1 @@
1
+ @import 'effective_products/base';
@@ -0,0 +1,9 @@
1
+ module Admin
2
+ class RingsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_products) }
5
+
6
+ include Effective::CrudController
7
+
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ module Effective
2
+ class RingPaymentsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+
5
+ include Effective::WizardController
6
+
7
+ resource_scope -> { EffectiveProducts.RingPayment.deep.where(owner: current_user) }
8
+
9
+ # Allow only 1 in-progress application at a time
10
+ before_action(only: [:new, :show], unless: -> { resource&.done? }) do
11
+ existing = resource_scope.in_progress.where.not(id: resource).first
12
+
13
+ if existing.present?
14
+ flash[:success] = "You have been redirected to your existing in progress payment"
15
+ redirect_to effective_products.ring_payment_build_path(existing, existing.next_step)
16
+ end
17
+ end
18
+
19
+ after_save do
20
+ flash.now[:success] = ''
21
+ end
22
+
23
+ private
24
+
25
+ def permitted_params
26
+ model = (params.key?(:effective_ring_payment) ? :effective_ring_payment : :ring_payment)
27
+ params.require(model).permit!.except(:status, :status_steps, :wizard_steps, :submitted_at)
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ module Admin
2
+ class EffectiveRingsDatatable < Effective::Datatable
3
+ filters do
4
+ scope :ready_to_issue
5
+ scope :issued
6
+ scope :all
7
+ end
8
+
9
+ datatable do
10
+ order :updated_at
11
+
12
+ col :updated_at, visible: false
13
+ col :created_at, visible: false
14
+ col :id, visible: false
15
+
16
+ col :created_at, as: :date
17
+ col :owner, search: :string
18
+
19
+ col :first_name
20
+ col :last_name
21
+
22
+ col :email
23
+ col :phone
24
+
25
+ col :member_number, label: 'Member #' do |ring|
26
+ ring.owner.try(:membership).try(:number)
27
+ end
28
+
29
+ col :shipping_address, label: 'Address'
30
+
31
+ col :size
32
+ col :metal
33
+ col :issued_at
34
+
35
+ actions_col
36
+ end
37
+
38
+ collection do
39
+ Effective::Ring.deep.all
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ # Dashboard Ring Payments
2
+ class EffectiveRingPaymentsDatatable < Effective::Datatable
3
+ datatable do
4
+ order :created_at
5
+
6
+ col :token, visible: false
7
+ col :created_at, visible: false
8
+
9
+ col(:submitted_at, label: 'Submitted on') do |resource|
10
+ resource.submitted_at&.strftime('%F') || 'Incomplete'
11
+ end
12
+
13
+ col :owner, visible: false, search: :string
14
+ col :status, visible: false
15
+ col :orders, action: :show
16
+
17
+ col(:issued_at, label: 'Issued on') do |resource|
18
+ resource.ring.issued_at&.strftime('%F') || '-'
19
+ end
20
+
21
+ actions_col(actions: []) do |resource|
22
+ if resource.draft?
23
+ dropdown_link_to('Continue', effective_products.ring_payment_build_path(resource, reource.next_step), 'data-turbolinks' => false)
24
+ dropdown_link_to('Delete', effective_products.ring_payment_path(resource), 'data-confirm': "Really delete #{resource}?", 'data-method': :delete)
25
+ else
26
+ dropdown_link_to('Show', effective_products.ring_payment_path(resource))
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ collection do
33
+ EffectiveProducts.RingPayment.deep.done.where(owner: current_user)
34
+ end
35
+
36
+ end
@@ -0,0 +1,3 @@
1
+ module EffectiveProductsHelper
2
+
3
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ # EffectiveProductsRingPayment
4
+ #
5
+ # Mark your owner model with effective_products_ring_payment to get all the includes
6
+
7
+ module EffectiveProductsRingPayment
8
+ extend ActiveSupport::Concern
9
+
10
+ module Base
11
+ def effective_products_ring_payment
12
+ include ::EffectiveProductsRingPayment
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ def effective_products_ring_payment?; true; end
18
+
19
+ def all_wizard_steps
20
+ const_get(:WIZARD_STEPS).keys
21
+ end
22
+
23
+ def required_wizard_steps
24
+ [:start, :billing, :checkout, :submitted]
25
+ end
26
+ end
27
+
28
+ included do
29
+ acts_as_purchasable_parent
30
+ acts_as_tokened
31
+
32
+ acts_as_statused(
33
+ :draft, # Just Started
34
+ :submitted # All done
35
+ )
36
+
37
+ acts_as_wizard(
38
+ start: 'Start',
39
+ ring: 'Select Ring',
40
+ summary: 'Review',
41
+ billing: 'Billing Address',
42
+ checkout: 'Checkout',
43
+ submitted: 'Submitted'
44
+ )
45
+
46
+ acts_as_purchasable_wizard
47
+
48
+ log_changes(except: :wizard_steps) if respond_to?(:log_changes)
49
+
50
+ # Application Namespace
51
+ belongs_to :owner, polymorphic: true
52
+ accepts_nested_attributes_for :owner
53
+
54
+ # Effective Namespace
55
+ has_many :rings, -> { order(:id) }, class_name: 'Effective::Ring', inverse_of: :ring_payment, dependent: :destroy
56
+ accepts_nested_attributes_for :rings, reject_if: :all_blank, allow_destroy: true
57
+
58
+ has_many :orders, -> { order(:id) }, as: :parent, class_name: 'Effective::Order', dependent: :nullify
59
+ accepts_nested_attributes_for :orders
60
+
61
+ effective_resource do
62
+ # Acts as Statused
63
+ status :string, permitted: false
64
+ status_steps :text, permitted: false
65
+
66
+ # Dates
67
+ submitted_at :datetime
68
+
69
+ # Acts as Wizard
70
+ wizard_steps :text, permitted: false
71
+
72
+ timestamps
73
+ end
74
+
75
+ scope :deep, -> { includes(:owner, :orders, :rings) }
76
+ scope :sorted, -> { order(:id) }
77
+
78
+ scope :in_progress, -> { where.not(status: [:submitted]) }
79
+ scope :done, -> { where(status: [:submitted]) }
80
+
81
+ scope :for, -> (user) { where(owner: user) }
82
+
83
+ # All Steps validations
84
+ validates :owner, presence: true
85
+
86
+ # Tickets Step
87
+ validate(if: -> { current_step == :ring }) do
88
+ self.errors.add(:rings, "can't be blank") unless present_rings.present?
89
+ end
90
+
91
+ # All Fees and Orders
92
+ def submit_fees
93
+ rings
94
+ end
95
+
96
+ def after_submit_purchased!
97
+ # Nothing to do yet
98
+ end
99
+
100
+ end
101
+
102
+ # Instance Methods
103
+ def to_s
104
+ 'ring payment'
105
+ end
106
+
107
+ def in_progress?
108
+ draft?
109
+ end
110
+
111
+ def done?
112
+ submitted?
113
+ end
114
+
115
+ def ring
116
+ rings.first
117
+ end
118
+
119
+ def build_ring
120
+ ring = rings.build(
121
+ owner: owner,
122
+ first_name: owner.try(:first_name),
123
+ last_name: owner.try(:last_name),
124
+ email: owner.try(:email),
125
+ phone: owner.try(:phone)
126
+ )
127
+
128
+ address = owner.try(:shipping_address) || owner.try(:billing_address)
129
+
130
+ if address.present?
131
+ ring.shipping_address = address
132
+ end
133
+
134
+ ring
135
+ end
136
+
137
+ def assign_pricing
138
+ price = case ring.metal
139
+ when '14k Yellow Gold' then 450_00
140
+ when 'Sterling Silver' then 175_00
141
+ when 'Titanium' then 50_00
142
+ else
143
+ raise "unexpected ring metal: #{ring.metal || 'none'}"
144
+ end
145
+
146
+ qb_item_name = "Chemist's Ring"
147
+ tax_exempt = false
148
+
149
+ ring.assign_attributes(price: price, qb_item_name: qb_item_name, tax_exempt: tax_exempt)
150
+ end
151
+
152
+ # After the configure Ring step
153
+ def ring!
154
+ assign_pricing() if ring.present?
155
+ save!
156
+ end
157
+
158
+ private
159
+
160
+ def present_rings
161
+ rings.reject(&:marked_for_destruction?)
162
+ end
163
+
164
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ class Ring < ActiveRecord::Base
5
+ SIZES = [3, 4, 5, 6, 7, 8]
6
+ TITANIUM_SIZES = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
7
+ METALS = ['14k Yellow Gold', 'Sterling Silver', 'Titanium']
8
+
9
+ acts_as_purchasable
10
+ acts_as_addressable :shipping
11
+
12
+ log_changes if respond_to?(:log_changes)
13
+
14
+ # This ring is charged to an owner
15
+ belongs_to :owner, polymorphic: true
16
+
17
+ # Through the ring_payment
18
+ belongs_to :ring_payment, polymorphic: true, optional: true
19
+
20
+ effective_resource do
21
+ first_name :string
22
+ last_name :string
23
+ phone :string
24
+ email :string
25
+
26
+ size :integer
27
+ metal :string
28
+
29
+ issued_at :datetime # Present when issued by an admin
30
+
31
+ # Acts as Purchasable
32
+ price :integer
33
+ qb_item_name :string
34
+ tax_exempt :boolean
35
+
36
+ timestamps
37
+ end
38
+
39
+ scope :deep, -> { includes(:owner) }
40
+ scope :ready_to_issue, -> { purchased.where(issued_at: nil) }
41
+ scope :issued, -> { where.not(issued_at: nil) }
42
+
43
+ validates :first_name, presence: true
44
+ validates :last_name, presence: true
45
+ validates :phone, presence: true
46
+ validates :email, presence: true, email: true
47
+
48
+ validates :metal, presence: true, inclusion: { in: METALS }
49
+
50
+ validates :size, presence: true
51
+ validates :size, inclusion: { in: TITANIUM_SIZES }, if: -> { metal == 'Titanium' }
52
+ validates :size, inclusion: { in: SIZES }, if: -> { metal != 'Titanium' }
53
+
54
+ def to_s
55
+ ["Chemist's Ring", (" - #{metal} size #{size}" if metal.present? && size.present?)].compact.join
56
+ end
57
+
58
+ def mark_as_issued!
59
+ update!(issued_at: Time.zone.now)
60
+ end
61
+
62
+ def issued?
63
+ issued_at.present?
64
+ end
65
+
66
+ # This is the Admin Save and Mark Paid action
67
+ def mark_paid!
68
+ raise('expected a blank ring payment') if ring_payment.present?
69
+
70
+ save!
71
+
72
+ order = Effective::Order.new(items: self, user: owner)
73
+ order.purchase!(skip_buyer_validations: true, email: false)
74
+
75
+ true
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,7 @@
1
+ module Effective
2
+ class RingPayment < ActiveRecord::Base
3
+ self.table_name = EffectiveProducts.ring_payments_table_name.to_s
4
+
5
+ effective_products_ring_payment
6
+ end
7
+ end
@@ -0,0 +1,40 @@
1
+ %table.table
2
+ %tbody
3
+ %tr
4
+ %th Owner
5
+ %td
6
+ - url = (edit_polymorphic_path(ring.owner) rescue "/admin/users/#{ring.owner.to_param}/edit")
7
+ = link_to(ring.owner, url)
8
+
9
+ %tr
10
+ %th Purchased Order
11
+ %td
12
+ - if ring.purchased_order.present?
13
+ = link_to(ring.purchased_order, effective_orders.admin_order_path(ring.purchased_order))
14
+ %tr
15
+ %th First Name
16
+ %td= ring.first_name
17
+ %tr
18
+ %th Last Name
19
+ %td= ring.last_name
20
+ %tr
21
+ %th Phone
22
+ %td= ring.phone
23
+ %tr
24
+ %th Email
25
+ %td= ring.email
26
+ %tr
27
+ %th Member Number
28
+ %td= ring.owner.try(:membership).try(:number) || '-'
29
+ %tr
30
+ %th Address
31
+ %td= ring.shipping_address.to_html
32
+ %tr
33
+ %th Size
34
+ %td= ring.size
35
+ %tr
36
+ %th Metal
37
+ %td= ring.metal
38
+ %tr
39
+ %th Issued At
40
+ %td= ring.issued_at
@@ -0,0 +1,10 @@
1
+ - all_steps_content = resource.try(:rich_text_all_steps_content)
2
+ - step_content = resource.try("rich_text_#{step}_content")
3
+
4
+ - if all_steps_content.present?
5
+ .card.mb-4
6
+ .card-body= all_steps_content
7
+
8
+ - if step_content.present?
9
+ .card.mb-4
10
+ .card-body= step_content
@@ -0,0 +1,27 @@
1
+ -# In progress payments
2
+ - existing = EffectiveProducts.RingPayment.in_progress.for(current_user).first
3
+ - datatable = EffectiveResources.best('EffectiveRingPaymentsDatatable').new(self, namespace: :effective)
4
+
5
+ - if existing.present?
6
+ %h2 In Progress Ring Payments
7
+
8
+ %p Your payment for Professional Ring is incomplete
9
+
10
+ %p
11
+ Please
12
+ = link_to("Continue payment for #{existing}", effective_products.ring_payment_build_path(existing, existing.next_step), 'data-turbolinks' => false, class: 'btn btn-primary')
13
+ or you can
14
+ = link_to('Abandon registration', effective_products.ring_payment_path(existing), 'data-confirm': "Really delete #{existing}?", 'data-method': :delete, class: 'btn btn-danger')
15
+ to start over.
16
+
17
+ %hr
18
+
19
+ %h2 Ring Payments
20
+
21
+ - if datatable.present?
22
+ = render_simple_datatable(datatable)
23
+ - else
24
+ %p You have no past ring payments. When you do, we'll show them here.
25
+
26
+ - if existing.blank?
27
+ %p= link_to 'Order a Professional Ring', effective_products.new_ring_payment_path, class: 'btn btn-primary'
@@ -0,0 +1,3 @@
1
+ .row
2
+ .col-lg-3.mb-3= render_wizard_sidebar(resource)
3
+ .col-lg-9= yield
@@ -0,0 +1,4 @@
1
+ - if ring_payment.submit_order&.purchased?
2
+ .card.mb-4
3
+ .card-body
4
+ = render(ring_payment.submit_order)
@@ -0,0 +1,29 @@
1
+ = card('Ring') do
2
+ - ring = ring_payment.ring
3
+
4
+ %table.table.table-sm
5
+ %tbody
6
+ %tr
7
+ %th Contact Info
8
+ %td
9
+ #{ring.first_name} #{ring.last_name}
10
+ %br
11
+ = mail_to(ring.email)
12
+ %br
13
+ = ring.phone
14
+
15
+ %tr
16
+ %th Address
17
+ %td= ring.shipping_address.to_html
18
+
19
+ %tr
20
+ %th Member Number
21
+ %td= ring_payment.owner.membership&.number || 'None'
22
+
23
+ %tr
24
+ %th Metal
25
+ %td= ring.metal
26
+
27
+ %tr
28
+ %th Size
29
+ %td Size #{ring.size}
@@ -0,0 +1,4 @@
1
+ = f.text_field :first_name
2
+ = f.text_field :last_name
3
+ = f.email_field :email
4
+ = f.phone_field :phone
@@ -0,0 +1,8 @@
1
+ .effective-ring-payment
2
+ - blacklist = ring_payment.class.required_wizard_steps + [:summary]
3
+ - steps = ring_payment.required_steps - blacklist
4
+
5
+ = render "effective/ring_payments/summary", ring_payment: ring_payment
6
+
7
+ - steps.select { |step| ring_payment.has_completed_step?(step) }.each do |partial|
8
+ = render "effective/ring_payments/#{partial}", ring_payment: ring_payment, step: partial
@@ -0,0 +1,31 @@
1
+ = card('Ring Payment') do
2
+ - ring = ring_payment.ring
3
+
4
+ %table.table.table-sm
5
+ %tbody
6
+ - if request.path.start_with?('/admin')
7
+ %tr
8
+ %th Ordered by
9
+ %td
10
+ - url = (polymorphic_admin_path(ring_payment.owner) rescue "/admin/users/#{ring_payment.owner.to_param}/edit")
11
+ = link_to(ring_payment.owner, url)
12
+ - else
13
+ %tr
14
+ %th Ordered by
15
+ %td= ring_payment.owner
16
+
17
+ - if ring_payment.orders.present?
18
+ %tr
19
+ %th Order
20
+ %td
21
+ - ring_payment.orders.each do |order|
22
+ = link_to(order, effective_orders.order_path(order))
23
+
24
+ - if ring_payment.was_submitted?
25
+ %tr
26
+ %th Submitted
27
+ %td= ring_payment.submitted_at.strftime('%F')
28
+
29
+ %tr
30
+ %th Issued
31
+ %td= ring.issued_at&.strftime('%F') || 'Not Issued'
@@ -0,0 +1,15 @@
1
+ = render 'layout' do
2
+ = render 'effective/ring_payments/content', resource: resource
3
+
4
+ - raise('expected owner to respond to billing_address') unless resource.owner.respond_to?(:billing_address)
5
+
6
+ = card('Billing Address') do
7
+ %p Please enter your billing address
8
+
9
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
10
+ = f.hidden_field :id
11
+
12
+ = f.fields_for(:owner, f.object.owner) do |fu|
13
+ = effective_address_fields(fu, :billing)
14
+
15
+ = f.save 'Save and Continue'
@@ -0,0 +1,6 @@
1
+ = render 'layout' do
2
+ = render 'effective/ring_payments/content', resource: resource
3
+
4
+ .card
5
+ .card-body
6
+ = render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:submitted), deferred_url: wizard_path(:checkout), declined_url: wizard_path(:checkout))
@@ -0,0 +1,18 @@
1
+ = render 'layout' do
2
+ = render 'effective/ring_payments/content', resource: resource
3
+
4
+ .card
5
+ .card-body
6
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
7
+ = f.hidden_field :id
8
+
9
+ = f.fields_for :rings, (f.object.ring || f.object.build_ring) do |fr|
10
+ = fr.hidden_field :ring_payment_id
11
+ = fr.hidden_field :ring_payment_type
12
+
13
+ = fr.hidden_field :owner_id
14
+ = fr.hidden_field :owner_type
15
+
16
+ = render('effective/ring_payments/ring_fields', f: fr)
17
+
18
+ = f.save 'Save and Continue'
@@ -0,0 +1,16 @@
1
+ = render 'layout' do
2
+ = render 'effective/ring_payments/content', resource: resource
3
+
4
+ .card
5
+ .card-body
6
+ %p Welcome #{current_user}!
7
+
8
+ %p You are about to purchase a Professional Ring.
9
+
10
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
11
+ = f.hidden_field :id
12
+
13
+ = f.hidden_field :owner_type
14
+ = f.hidden_field :owner_id
15
+
16
+ = f.save 'Save and Continue'
@@ -0,0 +1,15 @@
1
+ = render 'layout' do
2
+ = render 'effective/ring_payments/content', resource: resource
3
+
4
+ - raise('expected a submitted resource') unless resource.was_submitted?
5
+ - raise('expected a purchased resource submit_order') unless resource.submit_order&.purchased?
6
+
7
+ .alert.alert-warning.mb-4
8
+ Successfully paid on #{resource.submit_order.purchased_at.strftime('%F')}.
9
+
10
+ = link_to "Return to Dashboard", root_path, class: 'btn btn-lg btn-primary mb-4'
11
+
12
+ = render 'effective/ring_payments/ring_payment', ring_payment: resource
13
+ = render 'effective/ring_payments/orders', ring_payment: resource
14
+
15
+ = link_to 'Return to Dashboard', root_path, class: 'btn btn-lg btn-primary'
@@ -0,0 +1,8 @@
1
+ = render 'layout' do
2
+ = render 'effective/ring_payments/content', resource: resource
3
+
4
+ = render 'effective/ring_payments/ring_payment', ring_payment: resource
5
+
6
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
7
+ = f.hidden_field :id
8
+ = f.submit 'Save and Continue', class: 'btn btn-primary'
@@ -0,0 +1,39 @@
1
+ %h2 Contact Information
2
+
3
+ = f.text_field :first_name
4
+ = f.text_field :last_name
5
+ = f.email_field :email
6
+ = f.phone_field :phone
7
+ = f.static_field :member_number, value: f.object.owner&.membership&.number || 'None'
8
+
9
+ = effective_address_fields(f, :shipping)
10
+
11
+ %h2 Ring Information
12
+
13
+ %table.table
14
+ %thead
15
+ %th Ring Size
16
+ %th Composition
17
+ %th.order_price Cost
18
+ %tbody
19
+ %tr
20
+ %td 3-8
21
+ %td 14k Yellow Gold
22
+ %td.order_price $425.00 + GST
23
+ %tr
24
+ %td 3-8
25
+ %td Sterling Silver
26
+ %td.order_price $175.00 + GST
27
+ %tr
28
+ %td 3-13
29
+ %td Titanium
30
+ %td.order_price $50.00 + GST
31
+
32
+ %ul
33
+ %li 14k Yellow Gold and Sterling Silver rings are available in sizes 3 - 8.
34
+ %li Titanium rings are available in sizes 3 - 13.
35
+
36
+ %p Please select a ring metal and size:
37
+
38
+ = f.select :metal, Effective::Ring::METALS
39
+ = f.select :size, Effective::Ring::TITANIUM_SIZES
@@ -0,0 +1,18 @@
1
+ EffectiveProducts.setup do |config|
2
+ config.rings_table_name = :rings
3
+ config.ring_payments_table_name = :ring_payments
4
+ config.stamps_table_name = :stamps
5
+ config.stamp_payments_table_name = :stamp_payments
6
+
7
+ # Layout Settings
8
+ # Configure the Layout per controller, or all at once
9
+ # config.layout = { application: 'application', admin: 'admin' }
10
+
11
+ # Payment Wizard Settings
12
+ # config.ring_payments_class_name = 'Effective::RingPayment'
13
+ # config.stamp_payments_class_name = 'Effective::StampPayment'
14
+
15
+ # Use effective roles. Not sure what this does yet.
16
+ config.use_effective_roles = true
17
+
18
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.routes.draw do
4
+ mount EffectiveProducts::Engine => '/', as: 'effective_products'
5
+ end
6
+
7
+ EffectiveProducts::Engine.routes.draw do
8
+ # Public routes
9
+ scope module: 'effective' do
10
+ resources :ring_payments, only: [:new, :show, :destroy] do
11
+ resources :build, controller: :ring_payments, only: [:show, :update]
12
+ end
13
+ end
14
+
15
+ namespace :admin do
16
+ resources :ring_payments, except: [:new, :create, :show]
17
+
18
+ resources :rings, only: [:index, :show] do
19
+ post :mark_as_issued, on: :member
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,4 @@
1
+ class CreateEffectiveProducts < ActiveRecord::Migration[6.0]
2
+ def change
3
+ end
4
+ end
data/db/seeds.rb ADDED
@@ -0,0 +1 @@
1
+ puts "Running effective_products seeds"
@@ -0,0 +1,18 @@
1
+ module EffectiveProducts
2
+ class Engine < ::Rails::Engine
3
+ engine_name 'effective_products'
4
+
5
+ # Set up our default configuration options.
6
+ initializer 'effective_products.defaults', before: :load_config_initializers do |app|
7
+ eval File.read("#{config.root}/config/effective_products.rb")
8
+ end
9
+
10
+ # Include acts_as_addressable concern and allow any ActiveRecord object to call it
11
+ initializer 'effective_products.active_record' do |app|
12
+ ActiveSupport.on_load :active_record do
13
+ ActiveRecord::Base.extend(EffectiveProductsRingPayment::Base)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module EffectiveProducts
2
+ VERSION = '0.0.2'.freeze
3
+ end
@@ -0,0 +1,26 @@
1
+ require 'effective_resources'
2
+ require 'effective_datatables'
3
+ require 'effective_products/engine'
4
+ require 'effective_products/version'
5
+
6
+ module EffectiveProducts
7
+
8
+ def self.config_keys
9
+ [
10
+ :rings_table_name, :ring_payments_table_name, :ring_payment_class_name,
11
+ :stamps_table_name, :stamp_payments_table_name, :stamp_payment_class_name,
12
+ :layout, :use_effective_roles
13
+ ]
14
+ end
15
+
16
+ include EffectiveGem
17
+
18
+ def self.RingPayment
19
+ ring_payment_class_name&.constantize || Effective::RingPayment
20
+ end
21
+
22
+ def self.StampPayment
23
+ stamp_payment_class_name&.constantize || Effective::StampPayment
24
+ end
25
+
26
+ end
@@ -0,0 +1,30 @@
1
+ module EffectiveMemberships
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+
6
+ desc 'Creates an EffectiveProducts initializer in your application.'
7
+
8
+ source_root File.expand_path('../../templates', __FILE__)
9
+
10
+ def self.next_migration_number(dirname)
11
+ if not ActiveRecord::Base.timestamped_migrations
12
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
13
+ else
14
+ '%.3d' % (current_migration_number(dirname) + 1)
15
+ end
16
+ end
17
+
18
+ def copy_initializer
19
+ template ('../' * 3) + 'config/effective_products.rb', 'config/initializers/effective_products.rb'
20
+ end
21
+
22
+ def create_migration_file
23
+ @products_table_name = ':' + EffectiveProducts.products_table_name.to_s
24
+
25
+ migration_template ('../' * 3) + 'db/migrate/01_create_effective_products.rb.erb', 'db/migrate/create_effective_products.rb'
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ # Visit http://localhost:3000/rails/mailers
2
+
3
+ class EffectiveProductsMailerPreview < ActionMailer::Preview
4
+ end
@@ -0,0 +1,8 @@
1
+ namespace :effective_products do
2
+
3
+ # bundle exec rake effective_products:seed
4
+ task seed: :environment do
5
+ load "#{__dir__}/../../db/seeds.rb"
6
+ end
7
+
8
+ end
metadata ADDED
@@ -0,0 +1,281 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: effective_products
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Code and Effect
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-01-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: effective_bootstrap
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: effective_datatables
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 4.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 4.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: effective_orders
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: effective_resources
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: effective_roles
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: wicked
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: devise
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: haml-rails
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: pry-byebug
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: effective_test_bot
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: effective_developer
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: effective_email_templates
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ description: Highly customized product sales for member associations
210
+ email:
211
+ - info@codeandeffect.com
212
+ executables: []
213
+ extensions: []
214
+ extra_rdoc_files: []
215
+ files:
216
+ - MIT-LICENSE
217
+ - README.md
218
+ - Rakefile
219
+ - app/assets/config/effective_products_manifest.js
220
+ - app/assets/javascripts/effective_products.js
221
+ - app/assets/javascripts/effective_products/base.js
222
+ - app/assets/stylesheets/effective_products.scss
223
+ - app/assets/stylesheets/effective_products/base.scss
224
+ - app/controllers/admin/rings_controller.rb
225
+ - app/controllers/effective/ring_payments_controller.rb
226
+ - app/datatables/admin/effective_rings_datatable.rb
227
+ - app/datatables/effective_ring_payments_datatable.rb
228
+ - app/helpers/effective_products_helper.rb
229
+ - app/models/concerns/effective_products_ring_payment.rb
230
+ - app/models/effective/ring.rb
231
+ - app/models/effective/ring_payment.rb
232
+ - app/views/admin/rings/_ring.html.haml
233
+ - app/views/effective/ring_payments/_content.html.haml
234
+ - app/views/effective/ring_payments/_dashboard.html.haml
235
+ - app/views/effective/ring_payments/_layout.html.haml
236
+ - app/views/effective/ring_payments/_orders.html.haml
237
+ - app/views/effective/ring_payments/_ring.html.haml
238
+ - app/views/effective/ring_payments/_ring_fields.html.haml
239
+ - app/views/effective/ring_payments/_ring_payment.html.haml
240
+ - app/views/effective/ring_payments/_summary.html.haml
241
+ - app/views/effective/ring_payments/billing.html.haml
242
+ - app/views/effective/ring_payments/checkout.html.haml
243
+ - app/views/effective/ring_payments/ring.html.haml
244
+ - app/views/effective/ring_payments/start.html.haml
245
+ - app/views/effective/ring_payments/submitted.html.haml
246
+ - app/views/effective/ring_payments/summary.html.haml
247
+ - app/views/effective/rings/_fields.html.haml
248
+ - config/effective_products.rb
249
+ - config/routes.rb
250
+ - db/migrate/01_create_effective_products.rb.erb
251
+ - db/seeds.rb
252
+ - lib/effective_products.rb
253
+ - lib/effective_products/engine.rb
254
+ - lib/effective_products/version.rb
255
+ - lib/generators/effective_products/install_generator.rb
256
+ - lib/generators/templates/effective_products_mailer_preview.rb
257
+ - lib/tasks/effective_products_tasks.rake
258
+ homepage: https://github.com/code-and-effect/effective_products
259
+ licenses:
260
+ - MIT
261
+ metadata: {}
262
+ post_install_message:
263
+ rdoc_options: []
264
+ require_paths:
265
+ - lib
266
+ required_ruby_version: !ruby/object:Gem::Requirement
267
+ requirements:
268
+ - - ">="
269
+ - !ruby/object:Gem::Version
270
+ version: '0'
271
+ required_rubygems_version: !ruby/object:Gem::Requirement
272
+ requirements:
273
+ - - ">="
274
+ - !ruby/object:Gem::Version
275
+ version: '0'
276
+ requirements: []
277
+ rubygems_version: 3.1.2
278
+ signing_key:
279
+ specification_version: 4
280
+ summary: Highly customized product sales for member associations
281
+ test_files: []