mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-08 13:54:30 -04:00
Add support for auto bill on due date
This commit is contained in:
parent
1f11d88d6b
commit
a5fdb88d79
@ -8,6 +8,7 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Services\PaymentService;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceItem;
|
||||
use App\Models\Invitation;
|
||||
@ -18,13 +19,15 @@ class SendRecurringInvoices extends Command
|
||||
protected $description = 'Send recurring invoices';
|
||||
protected $mailer;
|
||||
protected $invoiceRepo;
|
||||
protected $paymentService;
|
||||
|
||||
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo)
|
||||
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, PaymentService $paymentService)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->mailer = $mailer;
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
$this->paymentService = $paymentService;
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
|
@ -934,6 +934,20 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
}
|
||||
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) {
|
||||
|
@ -194,16 +194,6 @@ class Payment extends EntityModel
|
||||
{
|
||||
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) {
|
||||
|
@ -159,14 +159,8 @@ class PaymentMethod extends EntityModel
|
||||
}
|
||||
}
|
||||
|
||||
public function getIpAddressAttribute($value)
|
||||
{
|
||||
return !$value?$value:inet_ntop($value);
|
||||
}
|
||||
|
||||
public function setIpAddressAttribute($value)
|
||||
{
|
||||
$this->attributes['ip_address'] = inet_pton($value);
|
||||
public function requiresDelayedAutoBill(){
|
||||
return $this->payment_type_id == PAYMENT_TYPE_DIRECT_DEBIT;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -815,7 +815,7 @@ class InvoiceRepository extends BaseRepository
|
||||
$recurInvoice->last_sent_date = date('Y-m-d');
|
||||
$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)) {
|
||||
// update the invoice reference to match its actual state
|
||||
// this is to ensure a 'payment received' email is sent
|
||||
|
@ -89,7 +89,7 @@ class PaymentService extends BaseService
|
||||
];
|
||||
|
||||
if ($input !== null) {
|
||||
$data['ip_address'] = \Request::ip();
|
||||
$data['ip'] = \Request::ip();
|
||||
}
|
||||
|
||||
if ($accountGateway->isGateway(GATEWAY_PAYPAL_EXPRESS) || $accountGateway->isGateway(GATEWAY_PAYPAL_PRO)) {
|
||||
@ -446,7 +446,7 @@ class PaymentService extends BaseService
|
||||
$accountGatewayToken->save();
|
||||
|
||||
$paymentMethod = $this->convertPaymentMethodFromGatewayResponse($tokenResponse, $accountGateway, $accountGatewayToken, $contactId);
|
||||
$paymentMethod->ip_address = \Request::ip();
|
||||
$paymentMethod->ip = \Request::ip();
|
||||
$paymentMethod->save();
|
||||
|
||||
} else {
|
||||
@ -650,8 +650,8 @@ class PaymentService extends BaseService
|
||||
$payment->payment_type_id = $this->detectCardType($card->getNumber());
|
||||
}
|
||||
|
||||
if (!empty($paymentDetails['ip_address'])) {
|
||||
$payment->ip_address = $paymentDetails['ip_address'];
|
||||
if (!empty($paymentDetails['ip'])) {
|
||||
$payment->ip = $paymentDetails['ip'];
|
||||
}
|
||||
|
||||
$savePaymentMethod = !empty($paymentMethod);
|
||||
@ -839,6 +839,38 @@ class PaymentService extends BaseService
|
||||
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
|
||||
$details = $this->getPaymentDetails($invitation, $accountGateway);
|
||||
$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)
|
||||
{
|
||||
$datatable = new PaymentDatatable( ! $clientPublicId, $clientPublicId);
|
||||
|
@ -13,26 +13,25 @@ class WePayAch extends Migration
|
||||
public function up()
|
||||
{
|
||||
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)
|
||||
{
|
||||
$table->string('bank_name')->nullable();
|
||||
$table->string('ip')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('payments', function($table)
|
||||
{
|
||||
$table->string('bank_name')->nullable();
|
||||
$table->string('ip')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('accounts', function($table)
|
||||
{
|
||||
$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)');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,11 +46,17 @@ class WePayAch extends Migration
|
||||
});
|
||||
|
||||
Schema::table('payments', function($table) {
|
||||
$table->dropColumn('ip_address');
|
||||
$table->dropColumn('bank_name');
|
||||
$table->dropColumn('ip');
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user