+ my $balance = 0;
+ my $invoices = [
+ map {
+ #not super efficient, we also run cust_bill_pay/cust_credited inside owed
+ my @payments_and_credits = sort {$b->_date <=> $a->_date} ($_->cust_bill_pay,$_->cust_credited);
+ my $owed = $_->owed;
+ $balance += $owed;
+ +{ 'invnum' => $_->invnum,
+ '_date' => $_->_date,
+ 'date' => time2str("%b %o, %Y", $_->_date),
+ 'date_short' => time2str("%m-%d-%Y", $_->_date),
+ 'previous' => sprintf('%.2f', ($_->previous)[0]),
+ 'charged' => sprintf('%.2f', $_->charged),
+ 'owed' => sprintf('%.2f', $owed),
+ 'balance' => sprintf('%.2f', $balance),
+ 'lastpay' => @payments_and_credits
+ ? time2str("%b %o, %Y", $payments_and_credits[0]->_date)
+ : '',
+ }
+ } @cust_bill
+ ];
+
+ return { 'error' => '',
+ 'balance' => $cust_main->balance,
+ 'money_char' => $conf->config("money_char") || '$',
+ 'invoices' => $invoices,
+ 'legacy_invoices' => [
+ map {
+ +{ 'legacyinvnum' => $_->legacyinvnum,
+ 'legacyid' => $_->legacyid,
+ '_date' => $_->_date,
+ 'date' => time2str("%b %o, %Y", $_->_date),
+ 'date_short' => time2str("%m-%d-%Y", $_->_date),
+ 'charged' => sprintf('%.2f', $_->charged),
+ 'has_content' => ( length($_->content_pdf)
+ || length($_->content_html) ),
+ }
+ }
+ @legacy_cust_bill
+ ],
+ };
+}
+
+sub list_payby {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ return {
+ 'payby' => [ map {
+ my $cust_payby = $_;
+ +{
+ map { $_ => $cust_payby->$_ }
+ qw( custpaybynum weight payby paymask paydate
+ payname paystate paytype
+ )
+ };
+ }
+ $cust_main->cust_payby
+ ],
+ };
+}
+
+sub insert_payby {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ #XXX payinfo1 + payinfo2 for CHEK?
+ #or take the opportunity to use separate, more well- named fields?
+ # my $payinfo;
+ # $p->{'payinfo1'} =~ /^([\dx]+)$/
+ # or return { 'error' => "illegal account number ". $p->{'payinfo1'} };
+ # my $payinfo1 = $1;
+ # $p->{'payinfo2'} =~ /^([\dx\.]+)$/ # . turned on by echeck-country CA ?
+ # or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} };
+ # my $payinfo2 = $1;
+ # $payinfo = $payinfo1. '@'. $payinfo2;
+
+ my $cust_payby = new FS::cust_payby {
+ 'custnum' => $custnum,
+ map { $_ => $p->{$_} } qw( weight payby payinfo paycvv paydate payname
+ paystate paytype payip
+ ),
+ };
+
+ my $error = $cust_payby->insert;
+ if ( $error ) {
+ return { 'error' => $error };
+ } else {
+ return { 'custpaybynum' => $cust_payby->custpaybynum };
+ }
+
+}
+
+sub update_payby {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $cust_payby = qsearchs('cust_payby', {
+ 'custnum' => $custnum,
+ 'custpaybynum' => $p->{'custpaybynum'},
+ })
+ or return { 'error' => 'unknown custpaybynum '. $p->{'custpaybynum'} };
+
+ foreach my $field (
+ qw( weight payby payinfo paycvv paydate payname paystate paytype payip )
+ ) {
+ next unless exists($p->{$field});
+ $cust_payby->set($field,$p->{$field});
+ }
+
+ my $error = $cust_payby->replace;
+ if ( $error ) {
+ return { 'error' => $error };
+ } else {
+ return { 'custpaybynum' => $cust_payby->custpaybynum };
+ }
+
+}
+
+sub verify_payby {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $cust_payby = qsearchs('cust_payby', {
+ 'custnum' => $custnum,
+ 'custpaybynum' => $p->{'custpaybynum'},
+ })
+ or return { 'error' => 'unknown custpaybynum '. $p->{'custpaybynum'} };
+
+ return { 'error' => $cust_payby->verify };
+
+}
+
+sub delete_payby {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my $cust_payby = qsearchs('cust_payby', {
+ 'custnum' => $custnum,
+ 'custpaybynum' => $p->{'custpaybynum'},
+ })
+ or return { 'error' => 'unknown custpaybynum '. $p->{'custpaybynum'} };
+
+ return { 'error' => $cust_payby->delete };