X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2FClientAPI%2FBulk.pm;fp=FS%2FFS%2FClientAPI%2FBulk.pm;h=ec617df7635693b149f358b8f15cc47b2b3607e6;hb=15e57a4859d967a13113602b112c4aa197ca6002;hp=0000000000000000000000000000000000000000;hpb=12ec6818dfd015042f516821c35ac68846b6baa0;p=freeside.git diff --git a/FS/FS/ClientAPI/Bulk.pm b/FS/FS/ClientAPI/Bulk.pm new file mode 100644 index 000000000..ec617df76 --- /dev/null +++ b/FS/FS/ClientAPI/Bulk.pm @@ -0,0 +1,384 @@ +package FS::ClientAPI::Bulk; + +use strict; + +use vars qw( $DEBUG $cache ); +use Date::Parse; +use FS::Record qw( qsearchs ); +use FS::Conf; +use FS::ClientAPI_SessionCache; +use FS::cust_main; +use FS::cust_pkg; +use FS::cust_svc; +use FS::svc_acct; +use FS::svc_external; +use FS::cust_recon; +use Data::Dumper; + +$DEBUG = 1; + +sub _cache { + $cache ||= new FS::ClientAPI_SessionCache ( { + 'namespace' => 'FS::ClientAPI::Agent', #yes, share session_ids + } ); +} + +sub _izoom_ftp_row_fixup { + my $hash = shift; + + my @addr_fields = qw( address1 address2 city state zip ); + my @fields = ( qw( agent_custid username _password first last ), + @addr_fields, + map { "ship_$_" } @addr_fields ); + + $hash->{$_} =~ s/[&\/\*'"]/_/g foreach @fields; + + #$hash->{action} = '' if $hash->{action} eq 'R'; #unsupported for ftp + + $hash->{refnum} = 1; #ahem + $hash->{country} = 'US'; + $hash->{ship_country} = 'US'; + $hash->{payby} = 'LECB'; + $hash->{payinfo} = $hash->{daytime}; + $hash->{ship_fax} = '' if ( !$hash->{sms} || $hash->{sms} eq 'F' ); + + my $has_ship = + grep { $hash->{"ship_$_"} && + (! $hash->{$_} || $hash->{"ship_$_"} ne $hash->{$_} ) + } + ( @addr_fields, 'fax' ); + + if ( $has_ship ) { + foreach ( @addr_fields, qw( first last ) ) { + $hash->{"ship_$_"} = $hash->{$_} unless $hash->{"ship_$_"}; + } + } + + delete $hash->{sms}; + + ''; + +}; + +sub _izoom_ftp_result { + my ($hash, $error) = @_; + my $cust_main = + qsearchs( 'cust_main', { 'agent_custid' => $hash->{agent_custid}, + 'agentnum' => $hash->{agentnum} + } + ); + + my $custnum = $cust_main ? $cust_main->custnum : ''; + my @response = ( $hash->{action}, $hash->{agent_custid}, $custnum ); + + if ( $error ) { + push @response, ( 'ERROR', $error ); + } else { + push @response, ( 'OK', 'OK' ); + } + + join( ',', @response ); + +} + +sub _izoom_ftp_badaction { + "Invalid action: $_[0] record: @_ "; +} + +sub _izoom_soap_row_fixup { _izoom_ftp_row_fixup(@_) }; + +sub _izoom_soap_result { + my ($hash, $error) = @_; + + if ( $hash->{action} eq 'R' ) { + if ( $error ) { + return "Please check errors:\n $error"; # odd extra space + } else { + return join(' ', "Everything ok.", $hash->{pkg}, $hash->{adjourn} ); + } + } + + my $pkg = $hash->{pkg} || $hash->{saved_pkg} || ''; + if ( $error ) { + return join(' ', $hash->{agent_custid}, $error ); + } else { + return join(' ', $hash->{agent_custid}, $pkg, $hash->{adjourn} ); + } + +} + +sub _izoom_soap_badaction { + "Unknown action '$_[13]' "; +} + +my %format = ( + 'izoom-ftp' => { + 'fields' => [ qw ( action agent_custid username _password + daytime ship_fax sms first last + address1 address2 city state zip + pkg adjourn ship_address1 ship_address2 + ship_city ship_state ship_zip ) ], + 'fixup' => sub { _izoom_ftp_row_fixup(@_) }, + 'result' => sub { _izoom_ftp_result(@_) }, + 'action' => sub { _izoom_ftp_badaction(@_) }, + }, + 'izoom-soap' => { + 'fields' => [ qw ( agent_custid username _password + daytime first last address1 address2 + city state zip pkg action adjourn + ship_fax sms ship_address1 ship_address2 + ship_city ship_state ship_zip ) ], + 'fixup' => sub { _izoom_soap_row_fixup(@_) }, + 'result' => sub { _izoom_soap_result(@_) }, + 'action' => sub { _izoom_soap_badaction(@_) }, + }, +); + +sub processrow { + my $p = shift; + + my $session = _cache->get($p->{'session_id'}) + or return { 'error' => "Can't resume session" }; #better error message + + my $conf = new FS::Conf; + my $format = $conf->config('selfservice-bulk_format', $session->{agentnum}) + || 'izoom-soap'; + my ( @row ) = @{ $p->{row} }; + + warn "processrow called with '". join("' '", @row). "'\n" if $DEBUG; + + return { 'error' => "unknown format: $format" } + unless exists $format{$format}; + + return { 'error' => "Invalid record record length: ". scalar(@row). + "record: @row " #sic + } + unless scalar(@row) == scalar(@{$format{$format}{fields}}); + + my %hash = ( 'agentnum' => $session->{agentnum} ); + my $error; + + foreach my $field ( @{ $format{ $format }{ fields } } ) { + $hash{$field} = shift @row; + } + + $error ||= &{ $format{ $format }{ fixup } }( \%hash ); + + # put in the fixup routine? + if ( 'R' eq $hash{action} ) { + warn "processing reconciliation\n" if $DEBUG; + $error ||= process_recon($hash{agentnum}, $hash{agent_custid}); + } elsif ( 'P' eq $hash{action} ) { + # do nothing + } elsif( 'D' eq $hash{action} ) { + $hash{promo_pkg} = 'disk-1-'. $session->{agent}; + } elsif ( 'S' eq $hash{action} ) { + $hash{promo_pkg} = 'disk-2-'. $session->{agent}; + $hash{saved_pkg} = $hash{pkg}; + $hash{pkg} = ''; + } else { + $error ||= &{ $format{ $format }{ action } }( @row ); + } + + warn "processing provision\n" if ($DEBUG && !$error && $hash{action} ne 'R'); + $error ||= provision( %hash ) unless $hash{action} eq 'R'; + + my $result = &{ $format{ $format }{ result } }( \%hash, $error ); + + warn "processrow returning '". join("' '", $result, $error). "'\n" + if $DEBUG; + + return { 'error' => $error, 'message' => $result }; + +} + +sub provision { + my %args = ( @_ ); + + delete $args{action}; + + my $cust_main = + qsearchs( 'cust_main', + { map { $_ => $args{$_} } qw ( agent_custid agentnum ) }, + ); + + unless ( $cust_main ) { + $cust_main = new FS::cust_main { %args }; + my $error = $cust_main->insert; + return $error if $error; + } + + my @pkgs = grep { $_->part_pkg->freq } $cust_main->ncancelled_pkgs; + if ( scalar(@pkgs) > 1 ) { + return "Invalid account, should not be more then one active package ". #sic + "but found: ". scalar(@pkgs). " packages."; + } + + my $part_pkg = qsearchs( 'part_pkg', { 'pkg' => $args{pkg} } ) + or return "Unknown pkgpart: $args{pkg}" + if $args{pkg}; + + + my $create_package = $args{pkg}; + if ( scalar(@pkgs) && $create_package ) { + my $pkg = pop(@pkgs); + + if ( $part_pkg->pkgpart != $pkg->pkgpart ) { + my @cust_bill_pkg = $pkg->cust_bill_pkg(); + if ( 1 == scalar(@cust_bill_pkg) ) { + my $cbp= pop(@cust_bill_pkg); + my $cust_bill = $cbp->cust_bill; + $cust_bill->delete(); #really? wouldn't a credit be better? + } + $pkg->cancel(); + } else { + $create_package = ''; + $pkg->setfield('adjourn', str2time($args{adjourn})); + my $error = $pkg->replace(); + return $error if $error; + } + } + + if ( $create_package ) { + my $cust_pkg = new FS::cust_pkg ( { + 'pkgpart' => $part_pkg->pkgpart, + 'adjourn' => str2time( $args{adjourn} ), + } ); + + my $svcpart = $part_pkg->svcpart('svc_acct'); + + my $svc_acct = new FS::svc_acct ( { + 'svcpart' => $svcpart, + 'username' => $args{username}, + '_password' => $args{_password}, + } ); + + my $error = $cust_main->order_pkg( cust_pkg => $cust_pkg, + svcs => [ $svc_acct ], + ); + return $error if $error; + } + + if ( $args{promo_pkg} ) { + my $part_pkg = + qsearchs( 'part_pkg', { 'promo_code' => $args{promo_pkg} } ) + or return "unknown pkgpart: $args{promo_pkg}"; + + my $svcpart = $part_pkg->svcpart('svc_external') + or return "unknown svcpart: svc_external"; + + my $cust_pkg = new FS::cust_pkg ( { + 'svcpart' => $svcpart, + 'pkgpart' => $part_pkg->pkgpart, + } ); + + my $svc_ext = new FS::svc_external ( { 'svcpart' => $svcpart } ); + + my $ticket_subject = 'Send setup disk to customer '. $cust_main->custnum; + my $error = $cust_main->order_pkg ( cust_pkg => $cust_pkg, + svcs => [ $svc_ext ], + noexport => 1, + ticket_subject => $ticket_subject, + ticket_queue => "disk-$args{agentnum}", + ); + return $error if $error; + } + + my $error = $cust_main->bill(); + return $error if $error; +} + +sub process_recon { + my ( $agentnum, $id ) = @_; + my @recs = split /;/, $id; + my $err = ''; + foreach my $rec ( @recs ) { + my @record = split /,/, $rec; + my $result = process_recon_record(@record, $agentnum); + $err .= "$result\n" if $result; + } + return $err; +} + +sub process_recon_record { + my ( $agent_custid, $username, $_password, $daytime, $first, $last, $address1, $address2, $city, $state, $zip, $pkg, $adjourn, $agentnum) = @_; + + warn "process_recon_record called with '". join("','", @_). "'\n" if $DEBUG; + + my ($cust_pkg, $package); + + my $cust_main = + qsearchs( 'cust_main', + { 'agent_custid' => $agent_custid, 'agentnum' => $agentnum }, + ); + + my $comments = ''; + if ( $cust_main ) { + my @cust_pkg = grep { $_->part_pkg->freq } $cust_main->ncancelled_pkgs; + if ( scalar(@cust_pkg) == 1) { + $cust_pkg = pop(@cust_pkg); + $package = $cust_pkg->part_pkg->pkg; + $comments = "$agent_custid wrong package, expected: $pkg found: $package" + if ( $pkg ne $package ); + } else { + $comments = "invalid account, should be one active package but found: ". + scalar(@cust_pkg). " packages."; + } + } else { + $comments = + "Customer not found agent_custid=$agent_custid, agentnum=$agentnum"; + } + + my $cust_recon = new FS::cust_recon( { + 'recondate' => time, + 'agentnum' => $agentnum, + 'first' => $first, + 'last' => $last, + 'address1' => $address1, + 'address2' => $address2, + 'city' => $city, + 'state' => $state, + 'zip' => $zip, + 'custnum' => $cust_main ? $cust_main->custnum : '', #really? + 'status' => $cust_main ? $cust_main->status : '', + 'pkg' => $package, + 'adjourn' => $cust_pkg ? $cust_pkg->adjourn : '', + 'agent_custid' => $agent_custid, # redundant? + 'agent_pkg' => $pkg, + 'agent_adjourn' => str2time($adjourn), + 'comments' => $comments, + } ); + + warn Dumper($cust_recon) if $DEBUG; + my $error = $cust_recon->insert; + return $error if $error; + + warn "process_recon_record returning $comments\n" if $DEBUG; + + $comments; + +} + +sub check_username { + my $p = shift; + + my $session = _cache->get($p->{'session_id'}) + or return { 'error' => "Can't resume session" }; #better error message + + my $svc_domain = qsearchs( 'svc_domain', { 'domain' => $p->{domain} } ) + or return { 'error' => 'Unknown domain '. $p->{domain} }; + + my $svc_acct = qsearchs( 'svc_acct', { 'username' => $p->{user}, + 'domsvc' => $svc_domain->svcnum, + }, + ); + + return { 'error' => $p->{user}. '@'. $p->{domain}. " alerady in use" } # sic + if $svc_acct; + + return { 'error' => '', + 'message' => $p->{user}. '@'. $p->{domain}. " is free" + }; +} + +1;