4 use FS::Daemon ':all'; #daemonize1 drop_root daemonize2 myexit logfile sig*
5 use FS::UID qw( adminsuidsetup );
6 use FS::Record qw( qsearch qsearchs );
11 my $user = shift or die &usage;
13 daemonize1('freeside-cdrrated');
17 adminsuidsetup($user);
19 logfile( "%%%FREESIDE_LOG%%%/cdrrated-log.". $FS::UID::datasrc );
23 our $conf = new FS::Conf;
25 die "not running; cdr-prerate conf option is off\n"
31 my @cdrtypenums = $conf->config('cdr-prerate-cdrtypenums');
33 $extra_sql .= ' AND cdrtypenum IN ('. join(',', @cdrtypenums ). ')';
36 #our %svcnum = (); # phonenum => svcnum
37 our %svc_phone = (); # phonenum => svc_phone
38 our %pkgnum = (); # phonenum => pkgnum
39 our %cust_pkg = (); # pkgnum => cust_pkg (NOT phonenum => cust_pkg!)
40 our %pkgpart = (); # phonenum => pkgpart
41 our %part_pkg = (); # pkgpart => part_pkg
43 #some false laziness w/freeside-cdrrewrited
51 'hashref' => { 'freesidestatus' => '' },
52 'extra_sql' => $extra_sql.
53 ' LIMIT 1024'. #arbitrary, but don't eat too much memory
61 #find the matching service - some weird false laziness w/svc_phone::get_cdrs
63 #in charged_party or src
64 #hmm... edge case; get_cdrs rating will match a src if a charged_party is
65 # present #but doesn't match a service...
66 my $number = $cdr->charged_party || $cdr->src;
68 #technically default_prefix. phonenum or phonenum (or default_prefix without the + . phonenum)
69 #but for now we're just assuming default_prefix is +1
70 my $prefix = '+1'; #$options{'default_prefix'};
72 $number = substr($number, length($prefix))
73 if $prefix eq substr($number, 0, length($prefix));
74 if ( $prefix && $prefix =~ /^\+(\d+)$/ ) {
76 $number = substr($number, length($prefix))
77 if $prefix eq substr($number, 0, length($prefix));
80 unless ( $svc_phone{$number} ) {
81 #only phone number matching supported right now
82 my $svc_phone = qsearchs('svc_phone', { 'phonenum' => $number } );
83 unless ( $svc_phone ) {
84 #XXX set freesideratestatus or something so we don't keep retrying?
88 $svc_phone{$number} = $svc_phone;
92 unless ( $pkgnum{$number} ) {
94 my $cust_pkg = $svc_phone{$number}->cust_svc->cust_pkg;
95 unless ( $cust_pkg ) {
96 #XXX unlinked svc_phone?
97 # warn and also set freesideratestatus or somesuch?
101 $pkgnum{$number} = $cust_pkg->pkgnum;
102 $cust_pkg{$cust_pkg->pkgnum} ||= $cust_pkg;
106 unless ( $pkgpart{$number} ) {
108 #get the package, search through the part_pkg and linked for a voip_cdr def w/matching cdrtypenum (or no use_cdrtypenum)
110 grep { $_->plan eq 'voip_cdr'
111 && ( ! length($_->option_cacheable('use_cdrtypenum'))
112 || $_->option_cacheable('use_cdrtypenum')
113 eq $cdr->cdrtypenum #eq otherwise 0 matches ''
115 && ( ! length($_->option_cacheable('ignore_cdrtypenum'))
116 || $_->option_cacheable('ignore_cdrtypenum')
117 ne $cdr->cdrtypenum #ne otherwise 0 matches ''
121 $cust_pkg{ $pkgnum{$number} }->part_pkg->self_and_bill_linked;
124 #XXX no package for this CDR
125 # warn and also set freesideratestatus or somesuch?
128 } elsif ( scalar(@part_pkg) > 1 ) {
129 warn "multiple package could rate CDR ". $cdr->acctid. "\n";
130 # and also set freesideratestatus or somesuch?
134 $pkgpart{$number} = $part_pkg[0]->pkgpart;
135 $part_pkg{ $part_pkg[0]->pkgpart } ||= $part_pkg[0];
139 if ( $part_pkg{ $pkgpart{$number} }->option('min_included') ) {
140 #then we can't prerate this CDR
141 #some sort of warning?
142 # (sucks if you're depending on credit limit fraud warnings)
146 my $error = $cdr->rate(
147 'part_pkg' => $part_pkg{ $pkgpart{$number} },
148 'cust_pkg' => $cust_pkg{ $pkgnum{$number} },
149 'svcnum' => $svc_phone{$number}->svcnum,
152 warn "Can't prerate CDR ". $cdr->acctid. ' to '. $cdr->dst. ": $error";
153 #could be an included minutes CDR, so don't sleep 30;
156 #this could get expensive on a per-call basis
157 # trigger in a separate process with less frequency?
159 my $cust_main = $cust_pkg{ $pkgnum{$number} }->cust_main;
161 my $error = $cust_main->check_credit_limit;
163 #"should never happen" normally, but as a daemon, better to survive
164 # e.g. database going away and coming back and resume doing our thing
171 last if sigterm() || sigint();
175 myexit() if sigterm() || sigint();
176 sleep 5 unless $found;
183 $conf->exists('cdr-prerate');
187 die "Usage:\n\n freeside-cdrrewrited user\n";
192 freeside-cdrrated - Real-time daemon for CDR rating
200 Runs continuously, searches for CDRs and which can be pre-rated, and rates them.
204 cdr-prerate configuration setting