summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2015-09-21 02:02:21 -0700
committerIvan Kohler <ivan@freeside.biz>2015-09-21 02:02:21 -0700
commit16e69398896d4d1c0b9c1e5786bdb31b11a18519 (patch)
treee004e42edeebb684930c1c9b4e00ca4914932077 /FS
parentce5f4c2396fd43608b60171464831cf2b53cc367 (diff)
billing event to call web services, RT#35167
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Misc/DateTime.pm22
-rw-r--r--FS/FS/cust_pay.pm16
-rw-r--r--FS/FS/part_event.pm2
-rw-r--r--FS/FS/part_event/Action/http.pm85
-rw-r--r--FS/FS/part_event/Condition.pm1
-rw-r--r--FS/FS/part_event_option.pm3
6 files changed, 123 insertions, 6 deletions
diff --git a/FS/FS/Misc/DateTime.pm b/FS/FS/Misc/DateTime.pm
index 2fff906..56baec3 100644
--- a/FS/FS/Misc/DateTime.pm
+++ b/FS/FS/Misc/DateTime.pm
@@ -6,9 +6,10 @@ use Carp;
use Time::Local;
use Date::Parse;
use DateTime::Format::Natural;
+use Date::Format;
use FS::Conf;
-@EXPORT_OK = qw( parse_datetime day_end );
+@EXPORT_OK = qw( parse_datetime day_end iso8601 );
=head1 NAME
@@ -65,11 +66,22 @@ same date but 23:59:59 for the time.
=cut
sub day_end {
- my $time = shift;
+ my $time = shift;
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
- localtime($time);
- timelocal(59,59,23,$mday,$mon,$year);
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
+ localtime($time);
+ timelocal(59,59,23,$mday,$mon,$year);
+}
+
+=item iso8601 TIME
+
+Parses time as an integer UNIX timestamp and returns the ISO 8601 formatted
+date and time.
+
+=cut
+
+sub iso8601 {
+ time2str('%Y-%m-%dT%T', @_);
}
=back
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index 59d7774..cb39d43 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -409,6 +409,22 @@ sub insert {
warn "can't send payment receipt/statement: $error" if $error;
}
+ #run payment events immediately
+ my $due_cust_event = $self->cust_main->due_cust_event(
+ 'eventtable' => 'cust_pay',
+ 'objects' => [ $self ],
+ );
+ if ( !ref($due_cust_event) ) {
+ warn "Error searching for cust_pay billing events: $due_cust_event\n";
+ } else {
+ foreach my $cust_event (@$due_cust_event) {
+ next unless $cust_event->test_conditions;
+ if ( my $error = $cust_event->do_event() ) {
+ warn "Error running cust_pay billing event: $error\n";
+ }
+ }
+ }
+
'';
}
diff --git a/FS/FS/part_event.pm b/FS/FS/part_event.pm
index d15f35b..9a1144c 100644
--- a/FS/FS/part_event.pm
+++ b/FS/FS/part_event.pm
@@ -369,6 +369,7 @@ sub eventtable_labels {
'cust_pkg' => 'Package',
'cust_bill' => 'Invoice',
'cust_main' => 'Customer',
+ 'cust_pay' => 'Payment',
'cust_pay_batch' => 'Batch payment',
'cust_statement' => 'Statement', #too general a name here? "Invoice group"?
'svc_acct' => 'Login service',
@@ -408,6 +409,7 @@ sub eventtable_pkey {
'cust_main' => 'custnum',
'cust_bill' => 'invnum',
'cust_pkg' => 'pkgnum',
+ 'cust_pay' => 'paynum',
'cust_pay_batch' => 'paybatchnum',
'cust_statement' => 'statementnum',
'svc_acct' => 'svcnum',
diff --git a/FS/FS/part_event/Action/http.pm b/FS/FS/part_event/Action/http.pm
new file mode 100644
index 0000000..b8715a7
--- /dev/null
+++ b/FS/FS/part_event/Action/http.pm
@@ -0,0 +1,85 @@
+package FS::part_event::Action::http;
+
+use strict;
+use base qw( FS::part_event::Action );
+use LWP::UserAgent;
+use HTTP::Request::Common;
+use JSON::XS;
+use FS::Misc::DateTime qw( iso8601 );
+
+#sub description { 'Send an HTTP or HTTPS GET or POST request'; }
+sub description { 'Send an HTTP or HTTPS POST request'; }
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1,
+ 'cust_pay' => 1,
+ },
+}
+
+sub option_fields {
+ (
+ 'method' => { label => 'Method',
+ type => 'select',
+ options => [qw( POST )], #GET )],
+ },
+ 'url' => { label => 'URL',
+ type => 'text',
+ size => 120,
+ },
+ 'ssl_no_verify' => { label => 'Skip SSL certificate validation',
+ type => 'checkbox',
+ },
+ 'encoding' => { label => 'Encoding',
+ type => 'select',
+ options => [qw( JSON )], #XML, Form, etc.
+ },
+ 'content' => { label => 'Content', #nneed better inline docs on format
+ type => 'textarea',
+ },
+ #'response_error_param' => 'Response error parameter',
+ );
+}
+
+sub default_weight { 57; }
+
+our %content_type = (
+ 'JSON' => 'application/json',
+);
+
+sub do_action {
+ my( $self, $object ) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my %content =
+ map {
+ /^\s*(\S+)\s+(.*)$/ or /()()/;
+ my( $field, $value_expression ) = ( $1, $2 );
+ my $value = eval $value_expression;
+ die $@ if $@;
+ ( $field, $value );
+ } split(/\n/, $self->option('content') );
+
+ my $content = encode_json( \%content );
+
+ my @lwp_opts = ();
+ push @lwp_opts, 'ssl_opts'=>{ 'verify_hostname'=>0 }
+ if $self->option('ssl_no_verify');
+ my $ua = LWP::UserAgent->new(@lwp_opts);
+
+ my $req = HTTP::Request::Common::POST(
+ $self->option('url'),
+ Content_Type => $content_type{ $self->option('encoding') },
+ Content => $content,
+ );
+
+ my $response = $ua->request($req);
+
+ die $response->status_line if $response->is_error;
+
+ my $response_json = decode_json( $response->content );
+ die $response_json->{error} if $response_json->{error}; #XXX response_error_param
+
+}
+
+1;
diff --git a/FS/FS/part_event/Condition.pm b/FS/FS/part_event/Condition.pm
index 60697c1..36fbe9a 100644
--- a/FS/FS/part_event/Condition.pm
+++ b/FS/FS/part_event/Condition.pm
@@ -52,6 +52,7 @@ sub eventtable_hashref {
{ 'cust_main' => 1,
'cust_bill' => 1,
'cust_pkg' => 1,
+ 'cust_pay' => 1,
'cust_pay_batch' => 1,
'cust_statement' => 1,
'svc_acct' => 1,
diff --git a/FS/FS/part_event_option.pm b/FS/FS/part_event_option.pm
index 09b7756..6df9e84 100644
--- a/FS/FS/part_event_option.pm
+++ b/FS/FS/part_event_option.pm
@@ -183,7 +183,8 @@ sub check {
$self->ut_numbern('optionnum')
|| $self->ut_foreign_key('eventpart', 'part_event', 'eventpart' )
|| $self->ut_text('optionname')
- || $self->ut_textn('optionvalue')
+ #|| $self->ut_textn('optionvalue')
+ || $self->ut_anything('optionvalue') #http.pm content has \n
;
return $error if $error;