effective_memberships 0.16.1 → 0.17.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: f05f20be1f211e1d0a3d17270820752b3015ac6790c635c32973c6c928b2f1b1
4
- data.tar.gz: b8273500b7e6af71968a2ed83195a5a380f0563ab2750c1b6a0624e87ec5c9e5
3
+ metadata.gz: '09fbb8a22bb53806fe272999514bc8cc4d8b8cf5784aaab5428777ffe43af61e'
4
+ data.tar.gz: cc6fd66a60a0db18c98a44849e3768e8c6d892b6af9cd297485b18f65290457b
5
5
  SHA512:
6
- metadata.gz: c298a6e323ce94af492a00ed1aa8addfd1777a1b2023f7715f66d0231868e813d09f59b13adcb14d80da2519b3bf294b52c4eb68ff08c1c466d07a5b257c1ccd
7
- data.tar.gz: c7bcfdb569352928092df90df69ec4fcd735354e71a68676721c139eb1f38b5f7bd0b874bdacadebb641309ff7c1cb425c570a31d806d9ffe227082650c5e7cf
6
+ metadata.gz: e286896d066c61cb0bb65b325837152d275f43c158e0f8df95302c36aee81ef1fcd3359efd37a71ec3f78b182a87fa0ee587c6ffe017d3971013af32b3647e67
7
+ data.tar.gz: 37c79f3c9a254275346c352832082bf9014690144720fd3ea2fef642dfe2740f3e72f3597870754f4c0076574ade33c65a1227d8fefa4f2325903aaacef9bd7f
@@ -35,9 +35,14 @@ module Admin
35
35
  col :qb_item_name, visible: false
36
36
 
37
37
  col :create_renewal_fees, visible: false
38
+ col :create_early_fees, visible: false
38
39
  col :create_late_fees, visible: false
39
40
  col :create_not_in_good_standing, visible: false
40
41
 
42
+ col :renewal_fee, as: :price, visible: false
43
+ col :early_fee, as: :price, visible: false
44
+ col :late_fee, as: :price, visible: false
45
+
41
46
  col :category_type, search: EffectiveMemberships.Category.category_types, visible: false
42
47
 
43
48
  col :optional_applicant_wizard_steps, label: 'Applicant Steps', visible: false
@@ -99,13 +99,19 @@ module EffectiveMembershipsCategory
99
99
  fee_payment_wizard_steps :text
100
100
  fee_payment_cpd_step_requirement :string
101
101
 
102
- # Renewals
102
+ # Renewal Fees
103
103
  create_renewal_fees :boolean
104
104
  renewal_fee :integer
105
105
 
106
+ # Early Fees
107
+ create_early_fees :boolean
108
+ early_fee :integer
109
+
110
+ # Late Fees
106
111
  create_late_fees :boolean
107
112
  late_fee :integer
108
113
 
114
+ # NIGS Status
109
115
  create_not_in_good_standing :boolean
110
116
 
111
117
  # Pricing
@@ -167,6 +173,19 @@ module EffectiveMembershipsCategory
167
173
  validates :qb_item_name, presence: true
168
174
  validates :tax_exempt, inclusion: { in: [true, false] }
169
175
  end
176
+
177
+ with_options(if: -> { create_renewal_fees? }) do
178
+ validates :renewal_fee, presence: true, numericality: { greater_than_or_equal_to: 0 }
179
+ end
180
+
181
+ with_options(if: -> { create_early_fees? }) do
182
+ validates :early_fee, presence: true, numericality: { less_than_or_equal_to: 0 }
183
+ end
184
+
185
+ with_options(if: -> { create_late_fees? }) do
186
+ validates :late_fee, presence: true, numericality: { greater_than_or_equal_to: 0 }
187
+ end
188
+
170
189
  end
171
190
 
172
191
  # Instance Methods
@@ -304,6 +304,34 @@ module EffectiveMembershipsOwner
304
304
  fee
305
305
  end
306
306
 
307
+ def build_early_fee(category:, period:)
308
+ raise('must have an existing membership') unless membership.present?
309
+
310
+ # Return existing but do not build yet
311
+ fee = fees.find { |fee| fee.fee_type == 'Early' && fee.period == period && fee.category_id == category.id && fee.category_type == category.class.name }
312
+ return fee if fee&.purchased?
313
+
314
+ # Only continue if there is an early renewal fee for the same period
315
+ renewal_fee = fees.find { |fee| fee.fee_type == 'Renewal' && fee.period == period && fee.category_id == category.id && fee.category_type == category.class.name }
316
+ return if fee.blank? && (renewal_fee.blank? || renewal_fee.early? == false)
317
+
318
+ # Build the early fee
319
+ fee ||= fees.build()
320
+
321
+ fee.assign_attributes(
322
+ fee_type: 'Early',
323
+ category: category,
324
+ period: period,
325
+ price: category.early_fee.to_i,
326
+ tax_exempt: category.tax_exempt,
327
+ qb_item_name: category.qb_item_name
328
+ )
329
+
330
+ after_build_fee(fee)
331
+
332
+ fee
333
+ end
334
+
307
335
  def build_late_fee(category:, period:)
308
336
  raise('must have an existing membership') unless membership.present?
309
337
 
@@ -24,7 +24,7 @@ module EffectiveMembershipsRegistrar
24
24
  end
25
25
 
26
26
  def late_fee_date(period:)
27
- Time.zone.local(period.year, 2, 1) # Fees are late after February 1st
27
+ Time.zone.local(period.year, 2, 1) # Fees are late after February 1st.
28
28
  raise('to be implemented by app registrar')
29
29
  end
30
30
 
@@ -33,6 +33,22 @@ module EffectiveMembershipsRegistrar
33
33
  raise('to be implemented by app registrar')
34
34
  end
35
35
 
36
+ # The early fee dates are calculated based on period
37
+ # The period is a date that starts on Jan 1st of a year.
38
+ # When Time.zone.now is December the period will be one ahead.
39
+
40
+ # Assumed to be the same as renewal_fee_date
41
+ def early_fee_date(period:)
42
+ Time.zone.local(period.year - 1, 12, 1) # Fees roll over every December 1st
43
+ raise('to be implemented by app registrar')
44
+ end
45
+
46
+ # Assumed to be just before the period actually starts
47
+ def early_fee_end_date(period:)
48
+ Time.zone.local(period.year - 1, 12, 31) # Early fees are destroyed on December 31st
49
+ raise('to be implemented by app registrar')
50
+ end
51
+
36
52
  # A renewal fee must be at least this old to be considered late?
37
53
  # Must be less than (late_fee_date - renewal_fee_date) but this is not enforced
38
54
  def min_late_duration
@@ -112,6 +128,10 @@ module EffectiveMembershipsRegistrar
112
128
  periods(from: from, to: to).reverse.map { |period| [period_end_on(date: period), period] }
113
129
  end
114
130
 
131
+ def early_fee_date_range(period:)
132
+ (early_fee_date(period: period).beginning_of_day)..(early_fee_end_date(period: period).beginning_of_day)
133
+ end
134
+
115
135
  # Category is required, statuses are optional
116
136
  def assign!(owner, categories:, statuses: nil, date: nil, number: nil)
117
137
  categories = Array(categories)
@@ -492,31 +512,48 @@ module EffectiveMembershipsRegistrar
492
512
  # rake effective_memberships:create_fees
493
513
  # Create Renewal and Late fees
494
514
  # Assigns NIGS
495
- def create_all_fees!(period: nil)
515
+ def create_all_fees!(period: nil, force: false)
496
516
  period ||= current_period
497
517
  memberships = Effective::Membership.deep.with_unpaid_fees_through(period)
498
518
 
499
519
  memberships.find_each do |membership|
500
- create_fees!(membership, period: period); GC.start
520
+ create_fees!(membership, period: period, force: force); GC.start
501
521
  end
502
522
 
503
523
  true
504
524
  end
505
525
 
506
- def create_fees!(resource, period: nil)
526
+ def create_fees!(resource, period: nil, force: false)
527
+ today = Time.zone.now.beginning_of_day
507
528
  period ||= current_period
508
529
 
509
530
  membership = (resource.class.respond_to?(:effective_memberships_owner?) ? resource.membership : resource)
510
531
  raise('expected a membership') unless membership.present? && membership.kind_of?(Effective::Membership)
511
532
 
512
533
  # Create Renewal Fees
513
- create_renewal_fees!(membership, period: period)
534
+ if force || today == renewal_fee_date(date: today)
535
+ create_renewal_fees!(membership, period: period)
536
+ end
537
+
538
+ # Create Early Fees
539
+ if force || today == early_fee_date(period: period)
540
+ create_early_fees!(membership, period: period)
541
+ end
542
+
543
+ # Destroy Early Fees
544
+ if force || today == early_fee_end_date(period: period)
545
+ destroy_early_fees!(membership, period: period)
546
+ end
514
547
 
515
548
  # Create Late Fees
516
- create_late_fees!(membership, period: period)
549
+ if force || today == late_fee_date(period: period)
550
+ create_late_fees!(membership, period: period)
551
+ end
517
552
 
518
553
  # Update Membership Status - Assign Not In Good Standing
519
- create_not_in_good_standing!(membership, period: period)
554
+ if force || today == not_in_good_standing_date(period: period)
555
+ create_not_in_good_standing!(membership, period: period)
556
+ end
520
557
 
521
558
  true
522
559
  end
@@ -536,6 +573,28 @@ module EffectiveMembershipsRegistrar
536
573
  end
537
574
  end
538
575
 
576
+ def create_early_fees!(membership, period:)
577
+ membership.categories.select(&:create_early_fees?).map do |category|
578
+ fee = membership.owner.build_early_fee(category: category, period: period)
579
+ next if fee.blank? || fee.persisted?
580
+
581
+ puts("Created early fee for #{membership.owner}") if debug?
582
+
583
+ fee.save!
584
+ end
585
+ end
586
+
587
+ def destroy_early_fees!(membership, period:)
588
+ membership.categories.select(&:create_early_fees?).map do |category|
589
+ fee = membership.owner.build_early_fee(category: category, period: period)
590
+ next if fee.blank? || fee.early? || fee.purchased? || fee.deferred?
591
+
592
+ puts("Destroyed early fee for #{membership.owner}") if debug?
593
+
594
+ fee.destroy!
595
+ end
596
+ end
597
+
539
598
  def create_late_fees!(membership, period:)
540
599
  membership.categories.select(&:create_late_fees?).map do |category|
541
600
  fee = membership.owner.build_late_fee(category: category, period: period)
@@ -548,7 +607,6 @@ module EffectiveMembershipsRegistrar
548
607
  end
549
608
 
550
609
  def create_not_in_good_standing!(membership, period:)
551
- return unless Time.zone.now.beginning_of_day == not_in_good_standing_date(period: period).beginning_of_day
552
610
  return unless membership.categories.any?(&:create_not_in_good_standing?)
553
611
 
554
612
  membership.owner.update_membership_status!
@@ -92,6 +92,11 @@ module Effective
92
92
  title.presence || default_title()
93
93
  end
94
94
 
95
+ def early?
96
+ return false if purchased?
97
+ EffectiveMemberships.Registrar.early_fee_date_range(period: period).cover?(Time.zone.now)
98
+ end
99
+
95
100
  def late?
96
101
  return false if late_on.blank?
97
102
  return false if purchased?
@@ -144,10 +149,6 @@ module Effective
144
149
  ['Prorated', 'Renewal'].include?(fee_type)
145
150
  end
146
151
 
147
- def renewal_fee?
148
- fee_type == 'Renewal'
149
- end
150
-
151
152
  def custom_fee?
152
153
  EffectiveMemberships.custom_fee_types.include?(fee_type)
153
154
  end
@@ -306,7 +306,7 @@ module Effective
306
306
 
307
307
  # Otherwise build fees right now
308
308
  EffectiveMemberships.Registrar.delete_fees!(self)
309
- EffectiveMemberships.Registrar.create_fees!(self, period: period)
309
+ EffectiveMemberships.Registrar.create_fees!(self, period: period, force: true)
310
310
 
311
311
  true
312
312
  end
@@ -15,5 +15,13 @@ module Effective
15
15
  Time.zone.local(period.year, 3, 1) # Membership are Not In Good Standing after March 1st
16
16
  end
17
17
 
18
+ def early_fee_date(period:)
19
+ Time.zone.local(period.year - 1, 12, 1) # Fees roll over every December 1st
20
+ end
21
+
22
+ def early_fee_end_date(period:)
23
+ Time.zone.local(period.year - 1, 12, 31) # Early fees are destroyed on December 31st
24
+ end
25
+
18
26
  end
19
27
  end
@@ -0,0 +1,14 @@
1
+ %h3= ets('effective_memberships.fees.early')
2
+
3
+ = f.check_box :create_early_fees, label: "Yes, #{ets('effective_memberships.fees.early')} should be created"
4
+
5
+ = f.show_if(:create_early_fees, true) do
6
+ - period = EffectiveMemberships.Registrar.current_period
7
+ - date = EffectiveMemberships.Registrar.early_fee_date(period: period)
8
+ - end_date = EffectiveMemberships.Registrar.early_fee_end_date(period: period)
9
+
10
+ %p Charged to members, between #{date.strftime('%B %e')} and #{end_date.strftime('%B %e')}, with outstanding renewal dues.
11
+
12
+ .row
13
+ .col-md-3
14
+ = f.price_field :early_fee, hint: 'Expecting a price less than zero. Please enter the early fee discount from the regular price'
@@ -1,5 +1,6 @@
1
- %h3 Late Fees
2
- = f.check_box :create_late_fees, label: 'Yes, late fees should be created'
1
+ %h3= ets('effective_memberships.fees.late')
2
+
3
+ = f.check_box :create_late_fees, label: "Yes, #{ets('effective_memberships.fees.late')} should be created"
3
4
 
4
5
  = f.show_if(:create_late_fees, true) do
5
6
  - period = EffectiveMemberships.Registrar.current_period
@@ -7,4 +8,4 @@
7
8
 
8
9
  %p Charged to members, on #{date.strftime('%B %e')}, with outstanding renewal dues or other fees.
9
10
  .row
10
- .col-3= f.price_field :late_fee
11
+ .col-md-3= f.price_field :late_fee
@@ -1,5 +1,5 @@
1
- %h3 Renewal Fees
2
- = f.check_box :create_renewal_fees, label: 'Yes, annual renewal fees should be created'
1
+ %h3= ets('effective_memberships.fees.renewal')
2
+ = f.check_box :create_renewal_fees, label: "Yes, #{ets('effective_memberships.fees.renewal')} should be created"
3
3
 
4
4
  = f.show_if(:create_renewal_fees, true) do
5
5
  - date = EffectiveMemberships.Registrar.renewal_fee_date(date: Time.zone.now)
@@ -1,7 +1,20 @@
1
1
  = effective_form_with(model: [:admin, category], engine: true) do |f|
2
2
  = render('admin/categories/fees_applicant', f: f)
3
- = render('admin/categories/fees_renewal', f: f)
4
- = render('admin/categories/fees_late', f: f)
3
+
4
+ - now = Time.zone.now
5
+
6
+ - if EffectiveMemberships.Registrar.renewal_fee_date(date: now).present?
7
+ = render('admin/categories/fees_renewal', f: f)
8
+
9
+ - if EffectiveMemberships.Registrar.early_fee_date(period: now).present?
10
+ = render('admin/categories/fees_early', f: f)
11
+
12
+ - if EffectiveMemberships.Registrar.late_fee_date(period: now).present?
13
+ = render('admin/categories/fees_late', f: f)
14
+
5
15
  = render('admin/categories/fees_additional', f: f)
6
- = render('admin/categories/fees_nigs', f: f)
16
+
17
+ - if EffectiveMemberships.Registrar.not_in_good_standing_date(period: now).present?
18
+ = render('admin/categories/fees_nigs', f: f)
19
+
7
20
  = f.submit
@@ -0,0 +1 @@
1
+ = f.check_box :declare_resignation, label: "Yes, I am applying for resignation from #{f.object.category}"
@@ -8,8 +8,6 @@
8
8
  = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
9
9
  = f.hidden_field :id
10
10
 
11
- = render_if_exists("effective/applicants/resignation_fields", f: f)
12
-
13
- = f.check_box :declare_resignation, label: "Yes, I am applying for resignation from #{f.object.category}"
11
+ = render("effective/applicants/resignation_fields", f: f)
14
12
 
15
13
  = f.save 'Save and Continue'
@@ -8,6 +8,7 @@ en:
8
8
  prorated: 'Prorated Fee'
9
9
  reinstatement: 'Reinstatement Fee'
10
10
  renewal: 'Renewal Fee'
11
+ early: 'Early Fee'
11
12
  late: 'Late Fee'
12
13
  discount: 'Discount Fee'
13
14
  admin: 'Admin Fee'
@@ -52,6 +52,9 @@ class CreateEffectiveMemberships < ActiveRecord::Migration[6.0]
52
52
  t.boolean :create_renewal_fees, default: false
53
53
  t.integer :renewal_fee
54
54
 
55
+ t.boolean :create_early_fees, default: false
56
+ t.integer :early_fee
57
+
55
58
  t.boolean :create_late_fees, default: false
56
59
  t.integer :late_fee
57
60
 
@@ -1,3 +1,3 @@
1
1
  module EffectiveMemberships
2
- VERSION = '0.16.1'
2
+ VERSION = '0.17.0'
3
3
  end
@@ -58,7 +58,7 @@ module EffectiveMemberships
58
58
  end
59
59
 
60
60
  def self.fee_types
61
- required = ['Applicant', 'Reinstatement', 'Prorated', 'Discount', 'Renewal', 'Late', 'Admin']
61
+ required = ['Applicant', 'Reinstatement', 'Prorated', 'Discount', 'Renewal', 'Early', 'Late', 'Admin']
62
62
  additional = Array(additional_fee_types)
63
63
 
64
64
  (required + additional).uniq.sort
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_memberships
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: 0.17.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: 2023-04-25 00:00:00.000000000 Z
11
+ date: 2023-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -367,6 +367,7 @@ files:
367
367
  - app/views/admin/applicants/_status.html.haml
368
368
  - app/views/admin/categories/_fees_additional.html.haml
369
369
  - app/views/admin/categories/_fees_applicant.html.haml
370
+ - app/views/admin/categories/_fees_early.html.haml
370
371
  - app/views/admin/categories/_fees_late.html.haml
371
372
  - app/views/admin/categories/_fees_nigs.html.haml
372
373
  - app/views/admin/categories/_fees_renewal.html.haml
@@ -458,6 +459,7 @@ files:
458
459
  - app/views/effective/applicants/_reinstatement.html.haml
459
460
  - app/views/effective/applicants/_reinstatement_fields.html.haml
460
461
  - app/views/effective/applicants/_resignation.html.haml
462
+ - app/views/effective/applicants/_resignation_fields.html.haml
461
463
  - app/views/effective/applicants/_select.html.haml
462
464
  - app/views/effective/applicants/_select_organization.html.haml
463
465
  - app/views/effective/applicants/_stamp.html.haml