effective_classifieds 0.3.0 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb0cfd942db297b12029c3482f9640b4016070b23293a8920f09cf7308b9f511
4
- data.tar.gz: 5677c6e19346e790344bf5dfaca5d411a06ec089e660a6fb0c13f19481b3393e
3
+ metadata.gz: 0aea0ae02dabe99cdca9471c23c158cdf007f31d7ae2fee4522103c2c3d678d7
4
+ data.tar.gz: 0f4272176f66730441f1fa4b46e6858dd784ae3b5c95ccd23ef001415f206dfd
5
5
  SHA512:
6
- metadata.gz: 2af2f02865c7961f045b3f259e6ba238b5b8f1485a72645771f278806a929887256971e8ca71e5d220e1e5f41cd04abd0354325ac72b380956d6e9b4e755b10d
7
- data.tar.gz: 0c80ae59bf92b7777cc0924a08c950a8687aee561ebe4901d4fe7023d8023f10ad03897eb68e29edcafac462fbc405911e9eccd8055a86b4c0b08e31bbe54017
6
+ metadata.gz: f62c3c700c9701fe44c1a2fc9be24f458c2aff875c0f32509d26f367ed8ad30e78b52e7cfb735bb34c5a8fb22908f6c88e81b0858fd3c51c9cda5de8e2242305
7
+ data.tar.gz: 2eece6889e8473c8acbc3c23a74b924894e89db234845ba0574784da9f0b3f6748ffb6e51596ee877c4f0c44a0a624c6fbfc16c423d901c95e182d73a4ee75e3
@@ -12,6 +12,8 @@ class Admin::EffectiveClassifiedWizardsDatatable < Effective::Datatable
12
12
  col :classified, search: :string
13
13
  col :owner
14
14
 
15
+ col :orders
16
+
15
17
  actions_col
16
18
  end
17
19
 
@@ -45,6 +45,8 @@ module Admin
45
45
  col :status, search: ['submitted', 'approved']
46
46
  end
47
47
 
48
+ col :purchased_order, visible: false
49
+
48
50
  actions_col do |classified|
49
51
  dropdown_link_to('View Classified', effective_classifieds.classified_path(classified), target: '_blank')
50
52
  end
@@ -10,7 +10,7 @@ class EffectiveClassifiedWizardsDatatable < Effective::Datatable
10
10
  wizard.submitted_at&.strftime('%F') || 'Incomplete'
11
11
  end
12
12
 
13
- col :classified, search: :string, label: 'Title'
13
+ col :classifieds, search: :string, label: 'Title'
14
14
 
15
15
  col :owner, visible: false, search: :string
16
16
 
@@ -30,7 +30,7 @@ class EffectiveClassifiedWizardsDatatable < Effective::Datatable
30
30
  end
31
31
 
32
32
  collection do
33
- EffectiveClassifieds.ClassifiedWizard.deep.where(owner: current_user).left_joins(:classified)
33
+ EffectiveClassifieds.ClassifiedWizard.deep.where(owner: current_user)
34
34
  end
35
35
 
36
36
  end
@@ -19,6 +19,7 @@ module EffectiveClassifiedsClassifiedWizard
19
19
 
20
20
  included do
21
21
  acts_as_tokened
22
+ acts_as_purchasable_parent
22
23
 
23
24
  acts_as_statused(
24
25
  :draft, # Just Started
@@ -29,9 +30,13 @@ module EffectiveClassifiedsClassifiedWizard
29
30
  start: 'Start',
30
31
  classified: 'Classified',
31
32
  summary: 'Review',
33
+ billing: 'Billing Address',
34
+ checkout: 'Checkout',
32
35
  submitted: 'Submitted'
33
36
  )
34
37
 
38
+ acts_as_purchasable_wizard
39
+
35
40
  log_changes(except: :wizard_steps) if respond_to?(:log_changes)
36
41
 
37
42
  # Application Namespace
@@ -39,8 +44,8 @@ module EffectiveClassifiedsClassifiedWizard
39
44
  accepts_nested_attributes_for :owner
40
45
 
41
46
  # Effective Namespace
42
- has_one :classified, class_name: 'Effective::Classified', inverse_of: :classified_wizard, dependent: :destroy
43
- accepts_nested_attributes_for :classified, reject_if: :all_blank, allow_destroy: true
47
+ has_many :classifieds, class_name: 'Effective::Classified', inverse_of: :classified_wizard, dependent: :destroy
48
+ accepts_nested_attributes_for :classifieds, reject_if: :all_blank, allow_destroy: true
44
49
 
45
50
  effective_resource do
46
51
  # Acts as Statused
@@ -50,13 +55,16 @@ module EffectiveClassifiedsClassifiedWizard
50
55
  # Dates
51
56
  submitted_at :datetime
52
57
 
58
+ # Pricing
59
+ price_category :string
60
+
53
61
  # Acts as Wizard
54
62
  wizard_steps :text, permitted: false
55
63
 
56
64
  timestamps
57
65
  end
58
66
 
59
- scope :deep, -> { includes(:classified) }
67
+ scope :deep, -> { includes(:classifieds) }
60
68
  scope :sorted, -> { order(:id) }
61
69
 
62
70
  scope :in_progress, -> { where.not(status: [:submitted]) }
@@ -71,6 +79,30 @@ module EffectiveClassifiedsClassifiedWizard
71
79
  with_options(if: -> { current_step == :classified }) do
72
80
  validates :classified, presence: true
73
81
  end
82
+
83
+ # All Fees and Orders
84
+ def submit_fees
85
+ classifieds
86
+ end
87
+
88
+ def after_submit_purchased!
89
+ # Nothing to do
90
+ end
91
+
92
+ # Overrides the acts_as_purchasable_wizard submit!
93
+ def submit!
94
+ raise('already submitted') if was_submitted?
95
+
96
+ if checkout_required?
97
+ raise('expected a purchased order') unless submit_order&.purchased?
98
+ wizard_steps[:checkout] ||= Time.zone.now
99
+ end
100
+
101
+ wizard_steps[:submitted] = Time.zone.now
102
+ classified.submit! unless classified.was_submitted?
103
+
104
+ submitted!
105
+ end
74
106
  end
75
107
 
76
108
  # Instance Methods
@@ -86,19 +118,39 @@ module EffectiveClassifiedsClassifiedWizard
86
118
  submitted?
87
119
  end
88
120
 
89
- # Called on the Review / Summary Step
90
- # But we actually want to submit it
91
- def summary!
92
- submit!
121
+ def checkout_required?
122
+ required_steps.include?(:billing) && required_steps.include?(:checkout)
123
+ end
124
+
125
+ def no_checkout_required?
126
+ required_steps.exclude?(:billing) && required_steps.exclude?(:checkout)
93
127
  end
94
128
 
95
- def submit!
96
- raise('already submitted') if was_submitted?
129
+ def classified
130
+ classifieds.first
131
+ end
97
132
 
98
- classified.submit! unless classified.was_submitted?
133
+ def build_classified
134
+ classifieds.build(owner: owner)
135
+ end
99
136
 
100
- wizard_steps[:submitted] = Time.zone.now
101
- submitted!
137
+ def assign_pricing
138
+ raise('to be implemented by including class')
139
+ # classified.assign_attributes(price: price, qb_item_name: qb_item_name, tax_exempt: tax_exempt)
140
+ end
141
+
142
+ # After the configure Classified step
143
+ def classified!
144
+ assign_pricing() if classified.present?
145
+ raise('expected classified to have a price') if classified.price.blank?
146
+
147
+ save!
148
+ end
149
+
150
+ # This is the review/summary step. Last one if we're not doing checkout
151
+ def summary!
152
+ return submit! if no_checkout_required?
153
+ save!
102
154
  end
103
155
 
104
156
  end
@@ -7,6 +7,7 @@ module Effective
7
7
  attr_accessor :current_user
8
8
 
9
9
  acts_as_slugged
10
+ acts_as_purchasable
10
11
  log_changes if respond_to?(:log_changes)
11
12
  acts_as_role_restricted if respond_to?(:acts_as_role_restricted)
12
13
 
@@ -17,6 +18,7 @@ module Effective
17
18
  # When submitted through the wizard
18
19
  belongs_to :classified_wizard, polymorphic: true, optional: true
19
20
 
21
+ has_one_attached :file
20
22
  has_rich_text :body
21
23
 
22
24
  acts_as_statused(
@@ -53,6 +55,10 @@ module Effective
53
55
 
54
56
  archived :boolean
55
57
 
58
+ price :integer
59
+ tax_exempt :boolean
60
+ qb_item_name :string
61
+
56
62
  timestamps
57
63
  end
58
64
 
@@ -103,7 +109,7 @@ module Effective
103
109
 
104
110
  # Automatically approve submissions created by admins outside the submissions wizard
105
111
  before_validation(if: -> { new_record? && classified_wizard.blank? }) do
106
- assign_attributes(status: :approved)
112
+ assign_attributes(status: :approved, price: 0)
107
113
  end
108
114
 
109
115
  validates :title, presence: true, length: { maximum: 200 }
@@ -133,6 +139,10 @@ module Effective
133
139
  self.errors.add(:category, 'is invalid') unless Array(EffectiveClassifieds.categories).include?(category)
134
140
  end
135
141
 
142
+ validate(if: -> { file.attached? && !Rails.env.test? }) do
143
+ self.errors.add(:file, 'expected a .pdf file') unless file.content_type == 'application/pdf'
144
+ end
145
+
136
146
  def to_s
137
147
  title.presence || 'New Classified'
138
148
  end
@@ -7,6 +7,12 @@
7
7
  = tab 'Access' do
8
8
  = render '/admin/classifieds/form_access', classified: classified
9
9
 
10
+ - if classified.classified_wizard.present?
11
+ = tab 'Orders' do
12
+ %h2 Orders
13
+ - datatable = Admin::EffectiveOrdersDatatable.new(parent: classified.classified_wizard, owner: classified.owner, user: classified.owner, total: false)
14
+ = render_datatable(datatable, simple: true)
15
+
10
16
  - if classified.respond_to?(:log_changes_datatable)
11
17
  = tab 'Logs' do
12
18
  = render_inline_datatable(classified.log_changes_datatable)
@@ -1,3 +1,4 @@
1
1
  = effective_form_with(model: [:admin, classified], engine: true) do |f|
2
2
  = render('effective/classifieds/fields', f: f, classified: f.object)
3
+
3
4
  = effective_submit(f)
@@ -0,0 +1 @@
1
+ - # Intentionally Blank
@@ -0,0 +1,4 @@
1
+ - if classified_wizard.submit_order&.purchased?
2
+ .card.mb-4
3
+ .card-body
4
+ = render(classified_wizard.submit_order)
@@ -0,0 +1,15 @@
1
+ = render 'layout' do
2
+ = render 'effective/classified_wizards/content', resource: resource
3
+
4
+ - raise('expected owner to respond to billing_address') unless resource.owner.respond_to?(:billing_address)
5
+
6
+ = card do
7
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
8
+ = f.hidden_field :id
9
+
10
+ = f.fields_for(:owner, f.object.owner) do |fo|
11
+ = effective_address_fields(fo, :billing)
12
+
13
+ %p.text-muted * Not all classifieds require a fee and we ask for a billing address for future use
14
+
15
+ = f.save 'Save and Continue'
@@ -0,0 +1,5 @@
1
+ = render 'layout' do
2
+ = render 'effective/classified_wizards/content', resource: resource
3
+
4
+ = card do
5
+ = render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:submitted), deferred_url: wizard_path(:checkout), declined_url: wizard_path(:checkout))
@@ -6,7 +6,7 @@
6
6
  = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
7
7
  = f.hidden_field :id
8
8
 
9
- = f.fields_for :classified, (f.object.classified || f.object.build_classified) do |fc|
9
+ = f.fields_for :classifieds, (f.object.classified || f.object.build_classified) do |fc|
10
10
  = fc.hidden_field :id
11
11
 
12
12
  = fc.hidden_field :owner_id, value: f.object.owner_id
@@ -14,4 +14,6 @@
14
14
 
15
15
  = render('effective/classifieds/fields', f: fc, classified: fc.object)
16
16
 
17
+ = render('effective/classified_wizards/additional_fields', f: f, classified_wizard: f.object)
18
+
17
19
  = f.save 'Save and Continue'
@@ -1,14 +1,29 @@
1
1
  = render 'layout' do
2
2
  = render 'effective/classified_wizards/content', resource: resource
3
3
 
4
+ - classified = resource.classified
5
+
4
6
  - raise('expected a submitted classified_wizard') unless resource.was_submitted?
7
+ - raise('expected a submitted classified') unless classified.was_submitted?
8
+
9
+ - if resource.checkout_required?
10
+ - raise('expected a purchased classified submit_order') unless resource.submit_order&.purchased?
5
11
 
6
12
  .alert.alert-warning.mb-4
7
- Successfully submitted on #{resource.submitted_at.strftime('%F')}.
13
+ This classified was submitted on #{classified.submitted_at.strftime('%F')}.
14
+
15
+ - unless classified.approved?
16
+ = card do
17
+ %p Your classified must still be approved before it will be displayed.
8
18
 
9
19
  = link_to "Return to Dashboard", root_path, class: 'btn btn-lg btn-primary mb-4'
10
20
 
11
21
  = render 'effective/classified_wizards/summary', classified_wizard: resource
12
22
  = render 'effective/classified_wizards/classified_wizard', classified_wizard: resource
13
23
 
24
+ - if resource.orders.present?
25
+ .mb-4
26
+ = collapse('Show orders...', card_class: 'my-2') do
27
+ = render 'effective/classified_wizards/orders', classified_wizard: resource
28
+
14
29
  = link_to "Return to Dashboard", root_path, class: 'btn btn-lg btn-primary'
@@ -17,14 +17,19 @@
17
17
  to
18
18
  #{classified.end_on&.strftime('%F')}
19
19
 
20
- - if classified.website.present?
20
+ - if classified.location.present?
21
21
  %tr
22
22
  %th Location
23
23
  %td= classified.location
24
24
 
25
25
  %tr
26
26
  %th Description
27
- %td= classified.body
27
+ %td= classified.body.to_s
28
+
29
+ - if classified.file.attached?
30
+ %tr
31
+ %th Attachment
32
+ %td= link_to(classified.file.filename, main_app.url_for(classified.file), target: '_blank')
28
33
 
29
34
  %tr
30
35
  %th Company
@@ -26,6 +26,8 @@
26
26
  - else
27
27
  = f.rich_text_area :body, label: 'Body content', hint: 'The main body content of your classified ad. Be sure to include all relevant information!'
28
28
 
29
+ = f.file_field :file, label: 'Attach PDF'
30
+
29
31
  %h2.mt-5.mb-0 Contact Information
30
32
  %p
31
33
  %small.text-muted Contact information will be displayed on the classified ad.
@@ -30,6 +30,12 @@ class CreateEffectiveClassifieds < ActiveRecord::Migration[6.1]
30
30
 
31
31
  t.boolean :archived, default: false
32
32
 
33
+ # Acts as purchasable
34
+ t.integer :purchased_order_id
35
+ t.integer :price
36
+ t.boolean :tax_exempt, default: false
37
+ t.string :qb_item_name
38
+
33
39
  t.timestamps
34
40
  end
35
41
 
@@ -52,6 +58,9 @@ class CreateEffectiveClassifieds < ActiveRecord::Migration[6.1]
52
58
  # Dates
53
59
  t.datetime :submitted_at
54
60
 
61
+ # Pricing
62
+ t.string :price_category
63
+
55
64
  t.datetime :updated_at
56
65
  t.datetime :created_at
57
66
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveClassifieds
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_classifieds
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-24 00:00:00.000000000 Z
11
+ date: 2022-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
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'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: effective_resources
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -195,12 +209,16 @@ files:
195
209
  - app/views/admin/classifieds/_form.html.haml
196
210
  - app/views/admin/classifieds/_form_access.html.haml
197
211
  - app/views/admin/classifieds/_form_classified.html.haml
212
+ - app/views/effective/classified_wizards/_additional_fields.html.haml
198
213
  - app/views/effective/classified_wizards/_classified.html.haml
199
214
  - app/views/effective/classified_wizards/_classified_wizard.html.haml
200
215
  - app/views/effective/classified_wizards/_content.html.haml
201
216
  - app/views/effective/classified_wizards/_dashboard.html.haml
202
217
  - app/views/effective/classified_wizards/_layout.html.haml
218
+ - app/views/effective/classified_wizards/_orders.html.haml
203
219
  - app/views/effective/classified_wizards/_summary.html.haml
220
+ - app/views/effective/classified_wizards/billing.html.haml
221
+ - app/views/effective/classified_wizards/checkout.html.haml
204
222
  - app/views/effective/classified_wizards/classified.html.haml
205
223
  - app/views/effective/classified_wizards/start.html.haml
206
224
  - app/views/effective/classified_wizards/submitted.html.haml