summaryrefslogtreecommitdiff
path: root/FS/FS/part_event
diff options
context:
space:
mode:
authorJonathan Prykop <jonathan@freeside.biz>2016-09-12 17:42:16 -0500
committerJonathan Prykop <jonathan@freeside.biz>2016-09-12 18:28:47 -0500
commit5cd19c26283661da5d242f031d1c81a4129782fd (patch)
treea9808b78e03804d5f6144511ae0467bbfbe9ea9f /FS/FS/part_event
parenta0e9787492ab306e94f41492ac7f59fb6a3e4802 (diff)
71720: Prevent billing events from running on holidays
Diffstat (limited to 'FS/FS/part_event')
-rw-r--r--FS/FS/part_event/Condition/holiday.pm90
1 files changed, 90 insertions, 0 deletions
diff --git a/FS/FS/part_event/Condition/holiday.pm b/FS/FS/part_event/Condition/holiday.pm
new file mode 100644
index 0000000..1639a17
--- /dev/null
+++ b/FS/FS/part_event/Condition/holiday.pm
@@ -0,0 +1,90 @@
+package FS::part_event::Condition::holiday;
+
+use strict;
+use base qw( FS::part_event::Condition );
+use DateTime;
+use DateTime::Format::ICal;
+use Tie::IxHash;
+
+# rules lifted from DateTime::Event::Holiday::US,
+# but their list is unordered, and contains duplicates and frivolous holidays
+# it's better for future development for us to use our own hard-coded list,
+# and the actual code beyond the list is just trivial use of DateTime::Format::ICal
+
+tie my %holidays, 'Tie::IxHash',
+ 'New Year\'s Day'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=1' }, # January 1
+ 'Birthday of Martin Luther King, Jr.'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=1;BYDAY=3mo' }, # Third Monday in January
+ 'Washington\'s Birthday'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=2;BYDAY=3mo' }, # Third Monday in February
+ 'Memorial Day'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=5;BYDAY=-1mo' }, # Last Monday in May
+ 'Independence Day'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=7;BYMONTHDAY=4' }, # July 4
+ 'Labor Day'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=1mo' }, # First Monday in September
+ 'Columbus Day'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=2mo' }, # Second Monday in October
+ 'Veterans Day'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=11;BYMONTHDAY=11' }, # November 11
+ 'Thanksgiving Day'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=4th' }, # Fourth Thursday in November
+ 'Christmas'
+ => { 'rule' => 'RRULE:FREQ=YEARLY;BYMONTH=12;BYMONTHDAY=25' }, # December 25
+;
+
+my $oneday = DateTime::Duration->new(days => 1);
+
+sub description {
+ "Do not run on holidays",
+}
+
+sub option_fields {
+ (
+ 'holidays' => {
+ label => 'Do not run on',
+ type => 'checkbox-multiple',
+ options => [ keys %holidays ],
+ option_labels => { map { $_ => $_ } keys %holidays },
+ default_value => { map { $_ => 1 } keys %holidays }
+ },
+ );
+}
+
+sub condition {
+ my( $self, $object, %opt ) = @_;
+ my $today = DateTime->from_epoch(
+ epoch => $opt{'time'} || time,
+ time_zone => 'local'
+ )->truncate( to => 'day' );
+
+ # if fri/mon, also check sat/sun respectively
+ # federal holidays on weekends "move" to nearest weekday
+ # eg Christmas 2016 is Mon Dec 26
+ # we'll check both, so eg both Dec 25 & 26 are holidays in 2016
+ my $offday;
+ if ($today->day_of_week == 1) {
+ $offday = $today->clone->subtract_duration($oneday);
+ } elsif ($today->day_of_week == 5) {
+ $offday = $today->clone->add_duration($oneday);
+ }
+
+ foreach my $holiday (keys %{$self->option('holidays')}) {
+ $holidays{$holiday}{'set'} ||=
+ DateTime::Format::ICal->parse_recurrence(
+ 'recurrence' => $holidays{$holiday}{'rule'}
+ );
+ my $set = $holidays{$holiday}{'set'};
+ return ''
+ if $set->contains($today) or $offday && $set->contains($offday);
+ }
+
+ return 1;
+
+}
+
+
+# no condition_sql
+
+1;