Add start to RecurrenceRule

This commit is contained in:
Eric Schultz 2021-06-08 14:34:54 -05:00 committed by Eric Schultz
parent e04fd12f78
commit 9aaa1e491e
6 changed files with 257 additions and 10 deletions

View file

@ -33,7 +33,8 @@ class Recurrence < ApplicationRecord # rubocop:disable Metrics/ClassLength
[
{
interval: recurring_donation.interval,
type: from_recurring_time_unit_to_recurrence(recurring_donation.time_unit)
type: from_recurring_time_unit_to_recurrence(recurring_donation.time_unit),
start: recurrence_start_date
}
]
end
@ -58,10 +59,9 @@ class Recurrence < ApplicationRecord # rubocop:disable Metrics/ClassLength
json.add_builder_expansion :nonprofit, :supporter
json.start_date start_date
json.recurrences recurrences do |rec|
json.(rec, :interval, :type)
json.start rec[:start].to_i
end
json.invoice_template do # rubocop:disable Metrics/BlockLength
@ -134,4 +134,18 @@ class Recurrence < ApplicationRecord # rubocop:disable Metrics/ClassLength
'year' => 'yearly'
}[time_unit]
end
def recurrence_start_date # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
paydate = recurring_donation.paydate
paydate = if paydate.nil?
(1..28).cover?(start_date.day) ? start_date.day : 28
else
paydate
end
if paydate < start_date.day
(start_date + 1.month).beginning_of_month + (paydate - 1).days
else
start_date.beginning_of_month + (paydate - 1).days
end
end
end

View file

@ -45,14 +45,21 @@ export type RecurrenceRule = {
* Interval of `type` for the event to recur
*/
interval: number;
/**
* The instant when the recurrence should be calculated from
*/
start: number;
/**
* The scale of the recurrence
*/
type: 'monthly' | 'year';
/**
* The the point after which the rule should not recur anymore.
* The point after which the rule should not recur anymore.
*/
until?: Date;
until?: number;
};

View file

@ -2,8 +2,8 @@
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
FactoryBot.define do
factory :recurrence do
FactoryBot.define do # rubocop:disable Metrics/BlockLength
factory :recurrence do # rubocop:disable Metrics/BlockLength
supporter { association :supporter_with_fv_poverty }
recurring_donation do
association(:rd_with_dedication_designation,
@ -13,5 +13,33 @@ FactoryBot.define do
supporter: supporter))
end
amount { 500 }
factory :recurrence_with_paydate_earlier_in_month do
recurring_donation do
association(:rd_with_dedication_designation,
nonprofit: supporter.nonprofit,
supporter: supporter,
donation: association(
:donation_with_dedication_designation,
nonprofit: supporter.nonprofit,
supporter: supporter
),
paydate: 3)
end
end
factory :recurrence_with_paydate_later_in_month do
recurring_donation do
association(:rd_with_dedication_designation,
nonprofit: supporter.nonprofit,
supporter: supporter,
donation: association(
:donation_with_dedication_designation,
nonprofit: supporter.nonprofit,
supporter: supporter
),
paydate: 5)
end
end
end
end

View file

@ -487,9 +487,10 @@ describe InsertRecurringDonation do
'object' => common_builder_expanded.merge({
'object' => 'recurrence',
'id' => match_houid('recur'),
'start_date' => Time.current,
'start_date' => Time.new(2020, 5, 4).utc.to_i,
'recurrences' => [
{
'start' => Time.new(2020, 5, 4).utc.to_i,
'interval' => 1,
'type' => 'monthly'
}],

View file

@ -119,9 +119,10 @@ describe UpdateRecurringDonations do
'object' => common_builder_expanded.merge({
'object' => 'recurrence',
'id' => orig_recurrence.id,
'start_date' => orig_recurrence.start_date,
'start_date' => orig_recurrence.start_date.to_i,
'recurrences' => [
{
'start' => orig_recurrence.start_date.beginning_of_day.to_i,
'interval' => 1,
'type' => 'monthly'
}],

View file

@ -62,6 +62,7 @@ RSpec.describe Recurrence, type: :model do
it {
is_expected.to have_attributes(recurrences: contain_exactly(
{
start: Time.current.beginning_of_day,
interval: 1,
type: 'monthly'
}
@ -82,15 +83,210 @@ RSpec.describe Recurrence, type: :model do
let(:recurrence) { create(:recurrence) }
let(:invoice_template) { subject['invoice_template'] }
it do # rubocop:disable RSpec/ExampleLength
is_expected.to match_json(
{
object: 'recurrence',
nonprofit: kind_of(Numeric),
supporter: kind_of(Numeric),
id: match_houid('recur'),
start_date: Time.current.to_i,
recurrences: [
{
start: Time.new(2020, 5, 4).to_i,
interval: 1,
type: 'monthly'
}
],
invoice_template: {
supporter: kind_of(Numeric),
amount: { 'cents' => 500, 'currency' => 'usd' },
payment_method: { 'type' => 'stripe' },
trx_assignments: [trx_assignment_json]
}
}
)
end
end
end
describe 'recurrence_with_paydate_later_in_month' do
subject { create(:recurrence_with_paydate_later_in_month) }
def trx_assignment_match # rubocop:disable Metrics/MethodLength
{
assignment_object: 'donation',
amount: 500,
dedication: {
contact: {
email: 'email@ema.com'
},
name: 'our loved one',
note: 'we miss them dearly',
type: 'memory'
},
designation: 'designated for soup kitchen'
}
end
def trx_assignment_json
trx_assignment_match.merge({ amount: { cents: 500, currency: 'usd' } })
end
def invoice_template_match
{
amount: 500,
supporter: an_instance_of(Supporter),
payment_method: {
type: 'stripe'
},
trx_assignments: [trx_assignment_match]
}
end
def invoice_template_json
invoice_template_match.merge(trx_assignments: [trx_assignment_json]).deep_stringify_keys
end
it {
is_expected.to have_attributes(
supporter: an_instance_of(Supporter),
nonprofit: an_instance_of(Nonprofit),
start_date: Time.current,
id: match_houid('recur')
)
}
it {
is_expected.to have_attributes(recurrences: contain_exactly(
{
start: Time.new(2020, 5, 5).utc,
interval: 1,
type: 'monthly'
}
))
}
it { is_expected.to be_persisted }
it do
is_expected.to have_attributes(
invoice_template: invoice_template_match
)
end
describe '.to_builder' do
subject { JSON.parse(recurrence.to_builder.target!) }
let(:recurrence) { create(:recurrence_with_paydate_later_in_month) }
let(:invoice_template) { subject['invoice_template'] }
it do
is_expected.to match_json({
object: 'recurrence',
nonprofit: kind_of(Numeric),
supporter: kind_of(Numeric),
id: match_houid('recur'),
start_date: Time.current,
start_date: Time.current.to_i,
recurrences: [
{
start: Time.new(2020, 5, 5).to_i,
interval: 1,
type: 'monthly'
}
],
invoice_template: { supporter: kind_of(Numeric),
amount: { 'cents' => 500, 'currency' => 'usd' },
payment_method: { 'type' => 'stripe' },
trx_assignments: [trx_assignment_json] }
})
end
end
end
describe 'recurrence_with_paydate_earlier_in_month' do
subject { create(:recurrence_with_paydate_earlier_in_month) }
def trx_assignment_match # rubocop:disable Metrics/MethodLength
{
assignment_object: 'donation',
amount: 500,
dedication: {
contact: {
email: 'email@ema.com'
},
name: 'our loved one',
note: 'we miss them dearly',
type: 'memory'
},
designation: 'designated for soup kitchen'
}
end
def trx_assignment_json
trx_assignment_match.merge({ amount: { cents: 500, currency: 'usd' } })
end
def invoice_template_match
{
amount: 500,
supporter: an_instance_of(Supporter),
payment_method: {
type: 'stripe'
},
trx_assignments: [trx_assignment_match]
}
end
def invoice_template_json
invoice_template_match.merge(trx_assignments: [trx_assignment_json]).deep_stringify_keys
end
it {
is_expected.to have_attributes(
supporter: an_instance_of(Supporter),
nonprofit: an_instance_of(Nonprofit),
start_date: Time.current,
id: match_houid('recur')
)
}
it {
is_expected.to have_attributes(recurrences: contain_exactly(
{
start: Time.new(2020, 6, 3).utc,
interval: 1,
type: 'monthly'
}
))
}
it { is_expected.to be_persisted }
it do
is_expected.to have_attributes(
invoice_template: invoice_template_match
)
end
describe '.to_builder' do
subject { JSON.parse(recurrence.to_builder.target!) }
let(:recurrence) { create(:recurrence_with_paydate_earlier_in_month) }
let(:invoice_template) { subject['invoice_template'] }
it do
is_expected.to match_json({
object: 'recurrence',
nonprofit: kind_of(Numeric),
supporter: kind_of(Numeric),
id: match_houid('recur'),
start_date: Time.current.to_i,
recurrences: [
{
start: Time.new(2020, 6, 3).to_i,
interval: 1,
type: 'monthly'
}