X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_pay.pm;h=5d31d2cde63ce7a4e5b42979db156187b1e73f0f;hb=8b33800764316232933f5996ac3fe9ea1793d944;hp=a86bbc23a06f9dc6944583031da673ec7ce84126;hpb=dcdf657e77ec7b46dc69e19a849a9c133123db7c;p=freeside.git diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm index a86bbc23a..5d31d2cde 100644 --- a/FS/FS/cust_pay.pm +++ b/FS/FS/cust_pay.pm @@ -8,6 +8,7 @@ use Text::Template; use FS::Misc qw(send_email); use FS::Record qw( dbh qsearch qsearchs ); use FS::cust_main_Mixin; +use FS::payinfo_Mixin; use FS::cust_bill; use FS::cust_bill_pay; use FS::cust_pay_refund; @@ -68,7 +69,9 @@ L and L for conversion functions. =item paymask - Masked payinfo (See L for how this works) -=item paybatch - text field for tracking card processing +=item paybatch - text field for tracking card processing or other batch grouping + +=item payunique - Optional unique identifer to prevent duplicate transactions. =item closed - books closed flag, empty or `Y' @@ -98,12 +101,15 @@ Adds this payment to the database. For backwards-compatibility and convenience, if the additional field invnum is defined, an FS::cust_bill_pay record for the full amount of the payment -will be created. In this case, custnum is optional. +will be created. In this case, custnum is optional. An hash of optional +arguments may be passed. Currently "manual" is supported. If true, a +payment receipt is sent instead of a statement when 'payment_receipt_email' +configuration option is set. =cut sub insert { - my $self = shift; + my ($self, %options) = @_; local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -116,8 +122,9 @@ sub insert { local $FS::UID::AutoCommit = 0; my $dbh = dbh; + my $cust_bill; if ( $self->invnum ) { - my $cust_bill = qsearchs('cust_bill', { 'invnum' => $self->invnum } ) + $cust_bill = qsearchs('cust_bill', { 'invnum' => $self->invnum } ) or do { $dbh->rollback if $oldAutoCommit; return "Unknown cust_bill.invnum: ". $self->invnum; @@ -157,17 +164,6 @@ sub insert { } } - if ( $self->paybatch =~ /^webui-/ ) { - my @cust_pay = qsearch('cust_pay', { - 'custnum' => $self->custnum, - 'paybatch' => $self->paybatch, - } ); - if ( scalar(@cust_pay) > 1 ) { - $dbh->rollback if $oldAutoCommit; - return "a payment with webui token ". $self->paybatch. " already exists"; - } - } - $dbh->commit or die $dbh->errstr if $oldAutoCommit; #false laziness w/ cust_credit::insert @@ -188,27 +184,36 @@ sub insert { && grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list ) { - my $receipt_template = new Text::Template ( - TYPE => 'ARRAY', - SOURCE => [ map "$_\n", $conf->config('payment_receipt_email') ], - ) or do { - warn "can't create payment receipt template: $Text::Template::ERROR"; - return ''; - }; + $cust_bill ||= ($cust_main->cust_bill)[-1]; #rather inefficient though? - my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list; + my $error; + if ( ( exists($options{'manual'}) && $options{'manual'} ) + || ! $conf->exists('invoice_html_statement') + || ! $cust_bill + ) { - my $payby = $self->payby; - my $payinfo = $self->payinfo; - $payby =~ s/^BILL$/Check/ if $payinfo; - $payinfo = $self->payinfo_masked if $payby eq 'CARD' || $payby eq 'CHEK'; - $payby =~ s/^CHEK$/Electronic check/; + my $receipt_template = new Text::Template ( + TYPE => 'ARRAY', + SOURCE => [ map "$_\n", $conf->config('payment_receipt_email') ], + ) or do { + warn "can't create payment receipt template: $Text::Template::ERROR"; + return ''; + }; - my $error = send_email( - 'from' => $conf->config('invoice_from'), #??? well as good as any - 'to' => \@invoicing_list, - 'subject' => 'Payment receipt', - 'body' => [ $receipt_template->fill_in( HASH => { + my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } + $cust_main->invoicing_list; + + my $payby = $self->payby; + my $payinfo = $self->payinfo; + $payby =~ s/^BILL$/Check/ if $payinfo; + $payinfo = $self->paymask if $payby eq 'CARD' || $payby eq 'CHEK'; + $payby =~ s/^CHEK$/Electronic check/; + + $error = send_email( + 'from' => $conf->config('invoice_from'), #??? well as good as any + 'to' => \@invoicing_list, + 'subject' => 'Payment receipt', + 'body' => [ $receipt_template->fill_in( HASH => { 'date' => time2str("%a %B %o, %Y", $self->_date), 'name' => $cust_main->name, 'paynum' => $self->paynum, @@ -216,10 +221,24 @@ sub insert { 'payby' => ucfirst(lc($payby)), 'payinfo' => $payinfo, 'balance' => $cust_main->balance, - } ) ], - ); + } ) ], + ); + + } else { + + my $queue = new FS::queue { + 'paynum' => $self->paynum, + 'job' => 'FS::cust_bill::queueable_email', + }; + $error = $queue->insert( + 'invnum' => $cust_bill->invnum, + 'template' => 'statement', + ); + + } + if ( $error ) { - warn "can't send payment receipt: $error"; + warn "can't send payment receipt/statement: $error"; } } @@ -375,6 +394,7 @@ sub check { || $self->ut_money('paid') || $self->ut_numbern('_date') || $self->ut_textn('paybatch') + || $self->ut_textn('payunique') || $self->ut_enum('closed', [ '', 'Y' ]) || $self->payinfo_check() ; @@ -388,6 +408,16 @@ sub check { $self->_date(time) unless $self->_date; + # UNIQUE index should catch this too, without race conditions, but this + # should give a better error message the other 99.9% of the time... + if ( length($self->payunique) + && qsearchs('cust_pay', { 'payunique' => $self->payunique } ) ) { + #well, it *could* be a better error message + return "duplicate transaction". + " - a payment with unique identifer ". $self->payunique. + " already exists"; + } + $self->SUPER::check; } @@ -427,7 +457,7 @@ sub batch_insert { my $errors = 0; my @errors = map { - my $error = $_->insert; + my $error = $_->insert( 'manual' => 1 ); if ( $error ) { $errors++; } else { @@ -518,23 +548,35 @@ sub cust_main { qsearchs( 'cust_main', { 'custnum' => $self->custnum } ); } -=item payinfo_masked +=back - Use $self->paymask +=head1 CLASS METHODS -Returns a "masked" payinfo field appropriate to the payment type. Masked characters are replaced by 'x'es. Use this to display publicly accessable account Information. +=over 4 -Credit Cards - Mask all but the last four characters. -Checks - Mask all but last 2 of account number and bank routing number. -Others - Do nothing, return the unmasked string. +=item unapplied_sql -=cut +Returns an SQL fragment to retreive the unapplied amount. -sub payinfo_masked { - my $self = shift; - return $self->paymask; -} +=cut +sub unapplied_sql { + #my $class = shift; + + "paid + - COALESCE( + ( SELECT SUM(amount) FROM cust_bill_pay + WHERE cust_pay.paynum = cust_bill_pay.paynum ) + ,0 + ) + - COALESCE( + ( SELECT SUM(amount) FROM cust_pay_refund + WHERE cust_pay.paynum = cust_pay_refund.paynum ) + ,0 + ) + "; + +} =back