Add start to RecurrenceRule
This commit is contained in:
parent
e04fd12f78
commit
9aaa1e491e
6 changed files with 257 additions and 10 deletions
|
@ -33,7 +33,8 @@ class Recurrence < ApplicationRecord # rubocop:disable Metrics/ClassLength
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
interval: recurring_donation.interval,
|
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
|
end
|
||||||
|
@ -58,10 +59,9 @@ class Recurrence < ApplicationRecord # rubocop:disable Metrics/ClassLength
|
||||||
|
|
||||||
json.add_builder_expansion :nonprofit, :supporter
|
json.add_builder_expansion :nonprofit, :supporter
|
||||||
|
|
||||||
json.start_date start_date
|
|
||||||
|
|
||||||
json.recurrences recurrences do |rec|
|
json.recurrences recurrences do |rec|
|
||||||
json.(rec, :interval, :type)
|
json.(rec, :interval, :type)
|
||||||
|
json.start rec[:start].to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
json.invoice_template do # rubocop:disable Metrics/BlockLength
|
json.invoice_template do # rubocop:disable Metrics/BlockLength
|
||||||
|
@ -134,4 +134,18 @@ class Recurrence < ApplicationRecord # rubocop:disable Metrics/ClassLength
|
||||||
'year' => 'yearly'
|
'year' => 'yearly'
|
||||||
}[time_unit]
|
}[time_unit]
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -45,14 +45,21 @@ export type RecurrenceRule = {
|
||||||
* Interval of `type` for the event to recur
|
* Interval of `type` for the event to recur
|
||||||
*/
|
*/
|
||||||
interval: number;
|
interval: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instant when the recurrence should be calculated from
|
||||||
|
*/
|
||||||
|
start: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The scale of the recurrence
|
* The scale of the recurrence
|
||||||
*/
|
*/
|
||||||
type: 'monthly' | 'year';
|
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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
|
# 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
|
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
|
||||||
FactoryBot.define do
|
FactoryBot.define do # rubocop:disable Metrics/BlockLength
|
||||||
factory :recurrence do
|
factory :recurrence do # rubocop:disable Metrics/BlockLength
|
||||||
supporter { association :supporter_with_fv_poverty }
|
supporter { association :supporter_with_fv_poverty }
|
||||||
recurring_donation do
|
recurring_donation do
|
||||||
association(:rd_with_dedication_designation,
|
association(:rd_with_dedication_designation,
|
||||||
|
@ -13,5 +13,33 @@ FactoryBot.define do
|
||||||
supporter: supporter))
|
supporter: supporter))
|
||||||
end
|
end
|
||||||
amount { 500 }
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -487,9 +487,10 @@ describe InsertRecurringDonation do
|
||||||
'object' => common_builder_expanded.merge({
|
'object' => common_builder_expanded.merge({
|
||||||
'object' => 'recurrence',
|
'object' => 'recurrence',
|
||||||
'id' => match_houid('recur'),
|
'id' => match_houid('recur'),
|
||||||
'start_date' => Time.current,
|
'start_date' => Time.new(2020, 5, 4).utc.to_i,
|
||||||
'recurrences' => [
|
'recurrences' => [
|
||||||
{
|
{
|
||||||
|
'start' => Time.new(2020, 5, 4).utc.to_i,
|
||||||
'interval' => 1,
|
'interval' => 1,
|
||||||
'type' => 'monthly'
|
'type' => 'monthly'
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -119,9 +119,10 @@ describe UpdateRecurringDonations do
|
||||||
'object' => common_builder_expanded.merge({
|
'object' => common_builder_expanded.merge({
|
||||||
'object' => 'recurrence',
|
'object' => 'recurrence',
|
||||||
'id' => orig_recurrence.id,
|
'id' => orig_recurrence.id,
|
||||||
'start_date' => orig_recurrence.start_date,
|
'start_date' => orig_recurrence.start_date.to_i,
|
||||||
'recurrences' => [
|
'recurrences' => [
|
||||||
{
|
{
|
||||||
|
'start' => orig_recurrence.start_date.beginning_of_day.to_i,
|
||||||
'interval' => 1,
|
'interval' => 1,
|
||||||
'type' => 'monthly'
|
'type' => 'monthly'
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -62,6 +62,7 @@ RSpec.describe Recurrence, type: :model do
|
||||||
it {
|
it {
|
||||||
is_expected.to have_attributes(recurrences: contain_exactly(
|
is_expected.to have_attributes(recurrences: contain_exactly(
|
||||||
{
|
{
|
||||||
|
start: Time.current.beginning_of_day,
|
||||||
interval: 1,
|
interval: 1,
|
||||||
type: 'monthly'
|
type: 'monthly'
|
||||||
}
|
}
|
||||||
|
@ -82,15 +83,210 @@ RSpec.describe Recurrence, type: :model do
|
||||||
let(:recurrence) { create(:recurrence) }
|
let(:recurrence) { create(:recurrence) }
|
||||||
let(:invoice_template) { subject['invoice_template'] }
|
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
|
it do
|
||||||
is_expected.to match_json({
|
is_expected.to match_json({
|
||||||
object: 'recurrence',
|
object: 'recurrence',
|
||||||
nonprofit: kind_of(Numeric),
|
nonprofit: kind_of(Numeric),
|
||||||
supporter: kind_of(Numeric),
|
supporter: kind_of(Numeric),
|
||||||
id: match_houid('recur'),
|
id: match_houid('recur'),
|
||||||
start_date: Time.current,
|
start_date: Time.current.to_i,
|
||||||
recurrences: [
|
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,
|
interval: 1,
|
||||||
type: 'monthly'
|
type: 'monthly'
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue