effective_products 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +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: []