mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-08 05:14:30 -04:00
Working on recurring expenses
This commit is contained in:
parent
b596845e1a
commit
1baaa76a00
@ -61,13 +61,21 @@ class SendRecurringInvoices extends Command
|
|||||||
public function fire()
|
public function fire()
|
||||||
{
|
{
|
||||||
$this->info(date('Y-m-d H:i:s') . ' Running SendRecurringInvoices...');
|
$this->info(date('Y-m-d H:i:s') . ' Running SendRecurringInvoices...');
|
||||||
$today = new DateTime();
|
|
||||||
|
|
||||||
if ($database = $this->option('database')) {
|
if ($database = $this->option('database')) {
|
||||||
config(['database.default' => $database]);
|
config(['database.default' => $database]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for counter resets
|
$this->resetCounters();
|
||||||
|
$this->createInvoices();
|
||||||
|
$this->billInvoices();
|
||||||
|
$this->createExpenses();
|
||||||
|
|
||||||
|
$this->info(date('Y-m-d H:i:s') . ' Done');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resetCounters()
|
||||||
|
{
|
||||||
$accounts = Account::where('reset_counter_frequency_id', '>', 0)
|
$accounts = Account::where('reset_counter_frequency_id', '>', 0)
|
||||||
->orderBy('id', 'asc')
|
->orderBy('id', 'asc')
|
||||||
->get();
|
->get();
|
||||||
@ -75,6 +83,11 @@ class SendRecurringInvoices extends Command
|
|||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
$account->checkCounterReset();
|
$account->checkCounterReset();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createInvoices()
|
||||||
|
{
|
||||||
|
$today = new DateTime();
|
||||||
|
|
||||||
$invoices = Invoice::with('account.timezone', 'invoice_items', 'client', 'user')
|
$invoices = Invoice::with('account.timezone', 'invoice_items', 'client', 'user')
|
||||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||||
@ -102,6 +115,11 @@ class SendRecurringInvoices extends Command
|
|||||||
}
|
}
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function billInvoices()
|
||||||
|
{
|
||||||
|
$today = new DateTime();
|
||||||
|
|
||||||
$delayedAutoBillInvoices = Invoice::with('account.timezone', 'recurring_invoice', 'invoice_items', 'client', 'user')
|
$delayedAutoBillInvoices = Invoice::with('account.timezone', 'recurring_invoice', 'invoice_items', 'client', 'user')
|
||||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS FALSE AND is_public IS TRUE
|
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS FALSE AND is_public IS TRUE
|
||||||
@ -124,8 +142,42 @@ class SendRecurringInvoices extends Command
|
|||||||
Auth::logout();
|
Auth::logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->info(date('Y-m-d H:i:s') . ' Done');
|
private function createExpenses()
|
||||||
|
{
|
||||||
|
$today = new DateTime();
|
||||||
|
|
||||||
|
$expenses = Expense::with('client')
|
||||||
|
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||||
|
->orderBy('id', 'asc')
|
||||||
|
->get();
|
||||||
|
$this->info(count($expenses).' recurring expenses(s) found');
|
||||||
|
|
||||||
|
foreach ($expenses as $expense) {
|
||||||
|
$shouldSendToday = $expense->shouldSendToday();
|
||||||
|
|
||||||
|
if (! $shouldSendToday) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info('Processing Expense: '. $expense->id);
|
||||||
|
|
||||||
|
$this->recurringExpenseRepo->createRecurringExpense($expense);
|
||||||
|
|
||||||
|
/*
|
||||||
|
$account = $expense->account;
|
||||||
|
//$account->loadLocalizationSettings($recurInvoice->client);
|
||||||
|
//Auth::loginUsingId($recurInvoice->user_id);
|
||||||
|
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
|
||||||
|
|
||||||
|
if ($invoice && ! $invoice->isPaid()) {
|
||||||
|
$this->info('Sending Invoice');
|
||||||
|
$this->mailer->sendInvoice($invoice);
|
||||||
|
}
|
||||||
|
Auth::logout();
|
||||||
|
*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,6 +144,10 @@ class RecurringExpenseController extends BaseController
|
|||||||
|
|
||||||
Session::flash('message', trans('texts.updated_recurring_expense'));
|
Session::flash('message', trans('texts.updated_recurring_expense'));
|
||||||
|
|
||||||
|
if (in_array(Input::get('action'), ['archive', 'delete', 'restore'])) {
|
||||||
|
return self::bulk();
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->to($recurringExpense->getRoute());
|
return redirect()->to($recurringExpense->getRoute());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +163,6 @@ class RecurringExpenseController extends BaseController
|
|||||||
Session::flash('message', $message);
|
Session::flash('message', $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->to('/recurring_expenses');
|
return $this->returnBulk($this->entityType, $action, $ids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,7 @@ Route::group(['middleware' => ['lookup:user', 'auth:user']], function () {
|
|||||||
Route::post('recurring_expenses', 'RecurringExpenseController@store');
|
Route::post('recurring_expenses', 'RecurringExpenseController@store');
|
||||||
Route::put('recurring_expenses/{recurring_expenses}', 'RecurringExpenseController@update');
|
Route::put('recurring_expenses/{recurring_expenses}', 'RecurringExpenseController@update');
|
||||||
Route::get('recurring_expenses/{recurring_expenses}/edit', 'RecurringExpenseController@edit');
|
Route::get('recurring_expenses/{recurring_expenses}/edit', 'RecurringExpenseController@edit');
|
||||||
|
Route::get('recurring_expenses/{recurring_expenses}', 'RecurringExpenseController@edit');
|
||||||
Route::post('recurring_expenses/bulk', 'RecurringExpenseController@bulk');
|
Route::post('recurring_expenses/bulk', 'RecurringExpenseController@bulk');
|
||||||
|
|
||||||
Route::get('documents/{documents}/{filename?}', 'DocumentController@get');
|
Route::get('documents/{documents}/{filename?}', 'DocumentController@get');
|
||||||
|
@ -11,11 +11,11 @@ use App\Events\QuoteInvitationWasEmailed;
|
|||||||
use App\Libraries\CurlUtils;
|
use App\Libraries\CurlUtils;
|
||||||
use App\Models\Activity;
|
use App\Models\Activity;
|
||||||
use App\Models\Traits\ChargesFees;
|
use App\Models\Traits\ChargesFees;
|
||||||
|
use App\Models\Traits\HasRecurrence;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Laracasts\Presenter\PresentableTrait;
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
use Utils;
|
use Utils;
|
||||||
use Carbon;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Invoice.
|
* Class Invoice.
|
||||||
@ -25,6 +25,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
use PresentableTrait;
|
use PresentableTrait;
|
||||||
use OwnedByClientTrait;
|
use OwnedByClientTrait;
|
||||||
use ChargesFees;
|
use ChargesFees;
|
||||||
|
use HasRecurrence;
|
||||||
use SoftDeletes {
|
use SoftDeletes {
|
||||||
SoftDeletes::trashed as parentTrashed;
|
SoftDeletes::trashed as parentTrashed;
|
||||||
}
|
}
|
||||||
@ -1117,122 +1118,6 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
return implode('<br/>', $dates);
|
return implode('<br/>', $dates);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function getRecurrenceRule()
|
|
||||||
{
|
|
||||||
$rule = '';
|
|
||||||
|
|
||||||
switch ($this->frequency_id) {
|
|
||||||
case FREQUENCY_WEEKLY:
|
|
||||||
$rule = 'FREQ=WEEKLY;';
|
|
||||||
break;
|
|
||||||
case FREQUENCY_TWO_WEEKS:
|
|
||||||
$rule = 'FREQ=WEEKLY;INTERVAL=2;';
|
|
||||||
break;
|
|
||||||
case FREQUENCY_FOUR_WEEKS:
|
|
||||||
$rule = 'FREQ=WEEKLY;INTERVAL=4;';
|
|
||||||
break;
|
|
||||||
case FREQUENCY_MONTHLY:
|
|
||||||
$rule = 'FREQ=MONTHLY;';
|
|
||||||
break;
|
|
||||||
case FREQUENCY_TWO_MONTHS:
|
|
||||||
$rule = 'FREQ=MONTHLY;INTERVAL=2;';
|
|
||||||
break;
|
|
||||||
case FREQUENCY_THREE_MONTHS:
|
|
||||||
$rule = 'FREQ=MONTHLY;INTERVAL=3;';
|
|
||||||
break;
|
|
||||||
case FREQUENCY_SIX_MONTHS:
|
|
||||||
$rule = 'FREQ=MONTHLY;INTERVAL=6;';
|
|
||||||
break;
|
|
||||||
case FREQUENCY_ANNUALLY:
|
|
||||||
$rule = 'FREQ=YEARLY;';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->end_date) {
|
|
||||||
$rule .= 'UNTIL=' . $this->getOriginal('end_date');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
public function shouldSendToday()
|
|
||||||
{
|
|
||||||
if (!$nextSendDate = $this->getNextSendDate()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->account->getDateTime() >= $nextSendDate;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function shouldSendToday()
|
|
||||||
{
|
|
||||||
if (! $this->user->confirmed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$account = $this->account;
|
|
||||||
$timezone = $account->getTimezone();
|
|
||||||
|
|
||||||
if (! $this->start_date || Carbon::parse($this->start_date, $timezone)->isFuture()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->end_date && Carbon::parse($this->end_date, $timezone)->isPast()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $this->last_sent_date) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
$date1 = new DateTime($this->last_sent_date);
|
|
||||||
$date2 = new DateTime();
|
|
||||||
$diff = $date2->diff($date1);
|
|
||||||
$daysSinceLastSent = $diff->format('%a');
|
|
||||||
$monthsSinceLastSent = ($diff->format('%y') * 12) + $diff->format('%m');
|
|
||||||
|
|
||||||
// check we don't send a few hours early due to timezone difference
|
|
||||||
if (Carbon::now()->format('Y-m-d') != Carbon::now($timezone)->format('Y-m-d')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check we never send twice on one day
|
|
||||||
if ($daysSinceLastSent == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($this->frequency_id) {
|
|
||||||
case FREQUENCY_WEEKLY:
|
|
||||||
return $daysSinceLastSent >= 7;
|
|
||||||
case FREQUENCY_TWO_WEEKS:
|
|
||||||
return $daysSinceLastSent >= 14;
|
|
||||||
case FREQUENCY_FOUR_WEEKS:
|
|
||||||
return $daysSinceLastSent >= 28;
|
|
||||||
case FREQUENCY_MONTHLY:
|
|
||||||
return $monthsSinceLastSent >= 1;
|
|
||||||
case FREQUENCY_TWO_MONTHS:
|
|
||||||
return $monthsSinceLastSent >= 2;
|
|
||||||
case FREQUENCY_THREE_MONTHS:
|
|
||||||
return $monthsSinceLastSent >= 3;
|
|
||||||
case FREQUENCY_SIX_MONTHS:
|
|
||||||
return $monthsSinceLastSent >= 6;
|
|
||||||
case FREQUENCY_ANNUALLY:
|
|
||||||
return $monthsSinceLastSent >= 12;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool|string
|
* @return bool|string
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
//use App\Events\ExpenseWasCreated;
|
//use App\Events\ExpenseWasCreated;
|
||||||
//use App\Events\ExpenseWasUpdated;
|
//use App\Events\ExpenseWasUpdated;
|
||||||
|
use App\Models\Traits\HasRecurrence;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Laracasts\Presenter\PresentableTrait;
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
use Utils;
|
use Utils;
|
||||||
@ -16,6 +17,7 @@ class RecurringExpense extends EntityModel
|
|||||||
// Expenses
|
// Expenses
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
use PresentableTrait;
|
use PresentableTrait;
|
||||||
|
use HasRecurrence;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
|
129
app/Models/Traits/HasRecurrence.php
Normal file
129
app/Models/Traits/HasRecurrence.php
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Traits;
|
||||||
|
|
||||||
|
use Carbon;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HasRecurrence
|
||||||
|
*/
|
||||||
|
trait HasRecurrence
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function shouldSendToday()
|
||||||
|
{
|
||||||
|
if (! $this->user->confirmed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = $this->account;
|
||||||
|
$timezone = $account->getTimezone();
|
||||||
|
|
||||||
|
if (! $this->start_date || Carbon::parse($this->start_date, $timezone)->isFuture()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->end_date && Carbon::parse($this->end_date, $timezone)->isPast()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->last_sent_date) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
$date1 = new DateTime($this->last_sent_date);
|
||||||
|
$date2 = new DateTime();
|
||||||
|
$diff = $date2->diff($date1);
|
||||||
|
$daysSinceLastSent = $diff->format('%a');
|
||||||
|
$monthsSinceLastSent = ($diff->format('%y') * 12) + $diff->format('%m');
|
||||||
|
|
||||||
|
// check we don't send a few hours early due to timezone difference
|
||||||
|
if (Carbon::now()->format('Y-m-d') != Carbon::now($timezone)->format('Y-m-d')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check we never send twice on one day
|
||||||
|
if ($daysSinceLastSent == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($this->frequency_id) {
|
||||||
|
case FREQUENCY_WEEKLY:
|
||||||
|
return $daysSinceLastSent >= 7;
|
||||||
|
case FREQUENCY_TWO_WEEKS:
|
||||||
|
return $daysSinceLastSent >= 14;
|
||||||
|
case FREQUENCY_FOUR_WEEKS:
|
||||||
|
return $daysSinceLastSent >= 28;
|
||||||
|
case FREQUENCY_MONTHLY:
|
||||||
|
return $monthsSinceLastSent >= 1;
|
||||||
|
case FREQUENCY_TWO_MONTHS:
|
||||||
|
return $monthsSinceLastSent >= 2;
|
||||||
|
case FREQUENCY_THREE_MONTHS:
|
||||||
|
return $monthsSinceLastSent >= 3;
|
||||||
|
case FREQUENCY_SIX_MONTHS:
|
||||||
|
return $monthsSinceLastSent >= 6;
|
||||||
|
case FREQUENCY_ANNUALLY:
|
||||||
|
return $monthsSinceLastSent >= 12;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getRecurrenceRule()
|
||||||
|
{
|
||||||
|
$rule = '';
|
||||||
|
|
||||||
|
switch ($this->frequency_id) {
|
||||||
|
case FREQUENCY_WEEKLY:
|
||||||
|
$rule = 'FREQ=WEEKLY;';
|
||||||
|
break;
|
||||||
|
case FREQUENCY_TWO_WEEKS:
|
||||||
|
$rule = 'FREQ=WEEKLY;INTERVAL=2;';
|
||||||
|
break;
|
||||||
|
case FREQUENCY_FOUR_WEEKS:
|
||||||
|
$rule = 'FREQ=WEEKLY;INTERVAL=4;';
|
||||||
|
break;
|
||||||
|
case FREQUENCY_MONTHLY:
|
||||||
|
$rule = 'FREQ=MONTHLY;';
|
||||||
|
break;
|
||||||
|
case FREQUENCY_TWO_MONTHS:
|
||||||
|
$rule = 'FREQ=MONTHLY;INTERVAL=2;';
|
||||||
|
break;
|
||||||
|
case FREQUENCY_THREE_MONTHS:
|
||||||
|
$rule = 'FREQ=MONTHLY;INTERVAL=3;';
|
||||||
|
break;
|
||||||
|
case FREQUENCY_SIX_MONTHS:
|
||||||
|
$rule = 'FREQ=MONTHLY;INTERVAL=6;';
|
||||||
|
break;
|
||||||
|
case FREQUENCY_ANNUALLY:
|
||||||
|
$rule = 'FREQ=YEARLY;';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->end_date) {
|
||||||
|
$rule .= 'UNTIL=' . $this->getOriginal('end_date');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function shouldSendToday()
|
||||||
|
{
|
||||||
|
if (!$nextSendDate = $this->getNextSendDate()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->account->getDateTime() >= $nextSendDate;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
@ -50,7 +50,7 @@ class UpdateDarkMode extends Migration
|
|||||||
$table->unsignedInteger('frequency_id');
|
$table->unsignedInteger('frequency_id');
|
||||||
$table->date('start_date')->nullable();
|
$table->date('start_date')->nullable();
|
||||||
$table->date('end_date')->nullable();
|
$table->date('end_date')->nullable();
|
||||||
$table->timestamp('last_created_date')->nullable();
|
$table->timestamp('last_sent_date')->nullable();
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user