Add support for auto bill on due date

This commit is contained in:
Joshua Dwire 2016-05-24 22:49:06 -04:00
parent 1f11d88d6b
commit a5fdb88d79
7 changed files with 114 additions and 31 deletions

View File

@ -8,6 +8,7 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use App\Ninja\Mailers\ContactMailer as Mailer; use App\Ninja\Mailers\ContactMailer as Mailer;
use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\InvoiceRepository;
use App\Services\PaymentService;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceItem; use App\Models\InvoiceItem;
use App\Models\Invitation; use App\Models\Invitation;
@ -18,13 +19,15 @@ class SendRecurringInvoices extends Command
protected $description = 'Send recurring invoices'; protected $description = 'Send recurring invoices';
protected $mailer; protected $mailer;
protected $invoiceRepo; protected $invoiceRepo;
protected $paymentService;
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo) public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, PaymentService $paymentService)
{ {
parent::__construct(); parent::__construct();
$this->mailer = $mailer; $this->mailer = $mailer;
$this->invoiceRepo = $invoiceRepo; $this->invoiceRepo = $invoiceRepo;
$this->paymentService = $paymentService;
} }
public function fire() public function fire()
@ -53,6 +56,39 @@ class SendRecurringInvoices extends Command
} }
} }
$delayedAutoBillInvoices = Invoice::with('account.timezone', 'recurring_invoice', 'invoice_items', 'client', 'user')
->leftJoin('invoices as recurring_invoice', 'invoices.recurring_invoice_id', '=', 'recurring_invoice.id')
->whereRaw('invoices.is_deleted IS FALSE AND invoices.deleted_at IS NULL AND invoices.is_recurring IS FALSE
AND invoices.balance > 0 AND invoices.due_date = ?
AND (recurring_invoice.auto_bill = ? OR (recurring_invoice.auto_bill != ? AND recurring_invoice.client_enable_auto_bill IS TRUE))',
array($today->format('Y-m-d'), AUTO_BILL_ALWAYS, AUTO_BILL_OFF))
->orderBy('invoices.id', 'asc')
->get();
$this->info(count($delayedAutoBillInvoices).' due recurring auto bill invoice instance(s) found');
foreach ($delayedAutoBillInvoices as $invoice) {
$autoBill = $invoice->getAutoBillEnabled();
$billNow = false;
if ($autoBill && !$invoice->isPaid()) {
$billNow = $invoice->account->auto_bill_on_due_date;
if (!$billNow) {
$paymentMethod = $this->invoiceService->getClientDefaultPaymentMethod($invoice->client);
if ($paymentMethod && $paymentMethod->requiresDelayedAutoBill()) {
$billNow = true;
}
}
}
$this->info('Processing Invoice '.$invoice->id.' - Should bill '.($billNow ? 'YES' : 'NO'));
if ($billNow) {
$this->paymentService->autoBillInvoice($invoice);
}
}
$this->info('Done'); $this->info('Done');
} }

View File

@ -934,6 +934,20 @@ class Invoice extends EntityModel implements BalanceAffecting
} }
return false; return false;
} }
public function getAutoBillEnabled() {
if (!$this->is_recurring) {
$recurInvoice = $this->recurring_invoice;
} else {
$recurInvoice = $this;
}
if (!$recurInvoice) {
return false;
}
return $recurInvoice->auto_bill == AUTO_BILL_ALWAYS || ($recurInvoice->auto_bill != AUTO_BILL_OFF && $recurInvoice->client_enable_auto_bill);
}
} }
Invoice::creating(function ($invoice) { Invoice::creating(function ($invoice) {

View File

@ -194,16 +194,6 @@ class Payment extends EntityModel
{ {
return $value ? str_pad($value, 4, '0', STR_PAD_LEFT) : null; return $value ? str_pad($value, 4, '0', STR_PAD_LEFT) : null;
} }
public function getIpAddressAttribute($value)
{
return !$value?$value:inet_ntop($value);
}
public function setIpAddressAttribute($value)
{
$this->attributes['ip_address'] = inet_pton($value);
}
} }
Payment::creating(function ($payment) { Payment::creating(function ($payment) {

View File

@ -159,14 +159,8 @@ class PaymentMethod extends EntityModel
} }
} }
public function getIpAddressAttribute($value) public function requiresDelayedAutoBill(){
{ return $this->payment_type_id == PAYMENT_TYPE_DIRECT_DEBIT;
return !$value?$value:inet_ntop($value);
}
public function setIpAddressAttribute($value)
{
$this->attributes['ip_address'] = inet_pton($value);
} }
} }

View File

@ -815,7 +815,7 @@ class InvoiceRepository extends BaseRepository
$recurInvoice->last_sent_date = date('Y-m-d'); $recurInvoice->last_sent_date = date('Y-m-d');
$recurInvoice->save(); $recurInvoice->save();
if ($recurInvoice->auto_bill == AUTO_BILL_ALWAYS || ($recurInvoice->auto_bill != AUTO_BILL_OFF && $recurInvoice->client_enable_auto_bill)) { if ($recurInvoice->getAutoBillEnabled() && !$recurInvoice->account->auto_bill_on_due_date) {
if ($this->paymentService->autoBillInvoice($invoice)) { if ($this->paymentService->autoBillInvoice($invoice)) {
// update the invoice reference to match its actual state // update the invoice reference to match its actual state
// this is to ensure a 'payment received' email is sent // this is to ensure a 'payment received' email is sent

View File

@ -89,7 +89,7 @@ class PaymentService extends BaseService
]; ];
if ($input !== null) { if ($input !== null) {
$data['ip_address'] = \Request::ip(); $data['ip'] = \Request::ip();
} }
if ($accountGateway->isGateway(GATEWAY_PAYPAL_EXPRESS) || $accountGateway->isGateway(GATEWAY_PAYPAL_PRO)) { if ($accountGateway->isGateway(GATEWAY_PAYPAL_EXPRESS) || $accountGateway->isGateway(GATEWAY_PAYPAL_PRO)) {
@ -446,7 +446,7 @@ class PaymentService extends BaseService
$accountGatewayToken->save(); $accountGatewayToken->save();
$paymentMethod = $this->convertPaymentMethodFromGatewayResponse($tokenResponse, $accountGateway, $accountGatewayToken, $contactId); $paymentMethod = $this->convertPaymentMethodFromGatewayResponse($tokenResponse, $accountGateway, $accountGatewayToken, $contactId);
$paymentMethod->ip_address = \Request::ip(); $paymentMethod->ip = \Request::ip();
$paymentMethod->save(); $paymentMethod->save();
} else { } else {
@ -650,8 +650,8 @@ class PaymentService extends BaseService
$payment->payment_type_id = $this->detectCardType($card->getNumber()); $payment->payment_type_id = $this->detectCardType($card->getNumber());
} }
if (!empty($paymentDetails['ip_address'])) { if (!empty($paymentDetails['ip'])) {
$payment->ip_address = $paymentDetails['ip_address']; $payment->ip = $paymentDetails['ip'];
} }
$savePaymentMethod = !empty($paymentMethod); $savePaymentMethod = !empty($paymentMethod);
@ -839,6 +839,38 @@ class PaymentService extends BaseService
return false; return false;
} }
if ($defaultPaymentMethod->requiresDelayedAutoBill()) {
$invoiceDate = DateTime::createFromFormat('Y-m-d', $invoice_date);
$minDueDate = clone $invoiceDate;
$minDueDate->modify('+10 days');
if (DateTime::create() < $minDueDate) {
// Can't auto bill now
return false;
}
$firstUpdate = \App\Models\Activities::where('invoice_id', '=', $invoice->id)
->where('activity_type_id', '=', ACTIVITY_TYPE_UPDATE_INVOICE)
->first();
if ($firstUpdate) {
$backup = json_decode($firstUpdate->json_backup);
if ($backup->balance != $invoice->balance || $backup->due_date != $invoice->due_date) {
// It's changed since we sent the email can't bill now
return false;
}
}
$invoicePayments = \App\Models\Activities::where('invoice_id', '=', $invoice->id)
->where('activity_type_id', '=', ACTIVITY_TYPE_CREATE_PAYMENT);
if ($invoicePayments->count()) {
// ACH requirements are strict; don't auto bill this
return false;
}
}
// setup the gateway/payment info // setup the gateway/payment info
$details = $this->getPaymentDetails($invitation, $accountGateway); $details = $this->getPaymentDetails($invitation, $accountGateway);
$details['customerReference'] = $token; $details['customerReference'] = $token;
@ -859,6 +891,18 @@ class PaymentService extends BaseService
} }
} }
public function getClientDefaultPaymentMethod($client) {
$this->getClientPaymentMethods($client);
$client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
if (!$accountGatewayToken) {
return false;
}
return $accountGatewayToken->default_payment_method;
}
public function getDatatable($clientPublicId, $search) public function getDatatable($clientPublicId, $search)
{ {
$datatable = new PaymentDatatable( ! $clientPublicId, $clientPublicId); $datatable = new PaymentDatatable( ! $clientPublicId, $clientPublicId);

View File

@ -13,26 +13,25 @@ class WePayAch extends Migration
public function up() public function up()
{ {
Schema::table('contacts', function(Blueprint $table) { Schema::table('contacts', function(Blueprint $table) {
$table->string('contact_key')->index()->default(null); $table->string('contact_key')->nullable()->default(null)->index()->unique();
}); });
Schema::table('payment_methods', function($table) Schema::table('payment_methods', function($table)
{ {
$table->string('bank_name')->nullable(); $table->string('bank_name')->nullable();
$table->string('ip')->nullable();
}); });
Schema::table('payments', function($table) Schema::table('payments', function($table)
{ {
$table->string('bank_name')->nullable(); $table->string('bank_name')->nullable();
$table->string('ip')->nullable();
}); });
Schema::table('accounts', function($table) Schema::table('accounts', function($table)
{ {
$table->boolean('auto_bill_on_due_date')->default(false); $table->boolean('auto_bill_on_due_date')->default(false);
}); });
DB::statement('ALTER TABLE `payments` ADD `ip_address` VARBINARY(16)');
DB::statement('ALTER TABLE `payment_methods` ADD `ip_address` VARBINARY(16)');
} }
/** /**
@ -43,15 +42,21 @@ class WePayAch extends Migration
public function down() public function down()
{ {
Schema::table('contacts', function(Blueprint $table) { Schema::table('contacts', function(Blueprint $table) {
$table->dropColumn('contact_key'); $table->dropColumn('contact_key');
}); });
Schema::table('payments', function($table) { Schema::table('payments', function($table) {
$table->dropColumn('ip_address'); $table->dropColumn('bank_name');
$table->dropColumn('ip');
}); });
Schema::table('payment_methods', function($table) { Schema::table('payment_methods', function($table) {
$table->dropColumn('ip_address'); $table->dropColumn('bank_name');
$table->dropColumn('ip');
});
Schema::table('accounts', function($table) {
$table->dropColumn('auto_bill_on_due_date');
}); });
} }
} }