mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
Fixes for timezone issues with recurring entities
This commit is contained in:
parent
38b77e72fe
commit
3bf56af37f
@ -204,9 +204,9 @@ class RecurringInvoiceController extends BaseController
|
||||
{
|
||||
$recurring_invoice = $this->recurring_invoice_repo->save($request->all(), RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
$offset = $recurring_invoice->client->timezone_offset();
|
||||
$recurring_invoice->next_send_date = Carbon::parse($recurring_invoice->next_send_date)->startOfDay()->addSeconds($offset);
|
||||
$recurring_invoice->saveQuietly();
|
||||
// $offset = $recurring_invoice->client->timezone_offset();
|
||||
// $recurring_invoice->next_send_date = Carbon::parse($recurring_invoice->next_send_date)->startOfDay()->addSeconds($offset);
|
||||
// $recurring_invoice->saveQuietly();
|
||||
|
||||
$recurring_invoice->service()
|
||||
->triggeredActions($request)
|
||||
|
@ -55,6 +55,10 @@ class StoreRecurringExpenseRequest extends Request
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
|
||||
$input['next_send_date_client'] = $input['next_send_date'];
|
||||
}
|
||||
|
||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
@ -66,6 +66,10 @@ class UpdateRecurringExpenseRequest extends Request
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
|
||||
$input['next_send_date_client'] = $input['next_send_date'];
|
||||
}
|
||||
|
||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
@ -67,6 +67,10 @@ class StoreRecurringInvoiceRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
|
||||
$input['next_send_date_client'] = $input['next_send_date'];
|
||||
}
|
||||
|
||||
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
|
||||
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
||||
}
|
||||
|
@ -61,6 +61,10 @@ class UpdateRecurringInvoiceRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
|
||||
$input['next_send_date_client'] = $input['next_send_date'];
|
||||
}
|
||||
|
||||
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
|
||||
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
||||
}
|
||||
|
@ -94,6 +94,8 @@ class RecurringExpensesCron
|
||||
$expense->save();
|
||||
|
||||
$recurring_expense->next_send_date = $recurring_expense->nextSendDate();
|
||||
$recurring_expense->next_send_date_client = $recurring_expense->next_send_date;
|
||||
|
||||
$recurring_expense->remaining_cycles = $recurring_expense->remainingCycles();
|
||||
$recurring_expense->save();
|
||||
}
|
||||
|
@ -105,6 +105,7 @@ class SendRecurring implements ShouldQueue
|
||||
nlog("updating recurring invoice dates");
|
||||
/* Set next date here to prevent a recurring loop forming */
|
||||
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
||||
$this->recurring_invoice->next_send_date_client = $this->recurring_invoice->nextSendDateClient();
|
||||
$this->recurring_invoice->remaining_cycles = $this->recurring_invoice->remainingCycles();
|
||||
$this->recurring_invoice->last_sent_date = now();
|
||||
|
||||
|
@ -667,6 +667,8 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
$offset -= $timezone->utc_offset;
|
||||
$offset += ($entity_send_time * 3600);
|
||||
|
||||
nlog("offset = {$offset}");
|
||||
|
||||
return $offset;
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,7 @@ class RecurringExpense extends BaseModel
|
||||
'last_sent_date',
|
||||
'next_send_date',
|
||||
'remaining_cycles',
|
||||
'next_send_date_client',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@ -153,6 +154,43 @@ class RecurringExpense extends BaseModel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function nextSendDateClient() :?Carbon
|
||||
{
|
||||
if (!$this->next_send_date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($this->frequency_id) {
|
||||
case RecurringInvoice::FREQUENCY_DAILY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addDay();
|
||||
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeek();
|
||||
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(2);
|
||||
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(4);
|
||||
case RecurringInvoice::FREQUENCY_MONTHLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthNoOverflow();
|
||||
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(2);
|
||||
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(3);
|
||||
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(4);
|
||||
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(6);
|
||||
case RecurringInvoice::FREQUENCY_ANNUALLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYear();
|
||||
case RecurringInvoice::FREQUENCY_TWO_YEARS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(2);
|
||||
case RecurringInvoice::FREQUENCY_THREE_YEARS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(3);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function remainingCycles() : int
|
||||
{
|
||||
if ($this->remaining_cycles == 0) {
|
||||
|
@ -108,6 +108,7 @@ class RecurringInvoice extends BaseModel
|
||||
'assigned_user_id',
|
||||
'exchange_rate',
|
||||
'vendor_id',
|
||||
'next_send_date_client',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@ -224,7 +225,7 @@ class RecurringInvoice extends BaseModel
|
||||
|
||||
public function nextSendDate() :?Carbon
|
||||
{
|
||||
if (!$this->next_send_date) {
|
||||
if (!$this->next_send_date_client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -236,7 +237,7 @@ class RecurringInvoice extends BaseModel
|
||||
|
||||
/* Lets set the next send date to now so we increment from today, rather than in the past*/
|
||||
if(Carbon::parse($this->next_send_date)->lt(now()->subDays(3)))
|
||||
$this->next_send_date = now()->format('Y-m-d');
|
||||
$this->next_send_date_client = now()->format('Y-m-d');
|
||||
|
||||
}
|
||||
|
||||
@ -244,34 +245,80 @@ class RecurringInvoice extends BaseModel
|
||||
As we are firing at UTC+0 if our offset is negative it is technically firing the day before so we always need
|
||||
to add ON a day - a day = 86400 seconds
|
||||
*/
|
||||
if($offset < 0)
|
||||
$offset += 86400;
|
||||
// if($offset < 0)
|
||||
// $offset += 86400;
|
||||
|
||||
switch ($this->frequency_id) {
|
||||
case self::FREQUENCY_DAILY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addDay()->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addDay()->addSeconds($offset);
|
||||
case self::FREQUENCY_WEEKLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeek()->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeek()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_WEEKS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(2)->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_FOUR_WEEKS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(4)->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(4)->addSeconds($offset);
|
||||
case self::FREQUENCY_MONTHLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthNoOverflow()->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthNoOverflow()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_THREE_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset);
|
||||
case self::FREQUENCY_FOUR_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset);
|
||||
case self::FREQUENCY_SIX_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(6)->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(6)->addSeconds($offset);
|
||||
case self::FREQUENCY_ANNUALLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYear()->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYear()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_YEARS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(2)->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_THREE_YEARS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(3)->addSeconds($offset);
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(3)->addSeconds($offset);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function nextSendDateClient() :?Carbon
|
||||
{
|
||||
if (!$this->next_send_date_client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* If this setting is enabled, the recurring invoice may be set in the past */
|
||||
|
||||
if($this->company->stop_on_unpaid_recurring) {
|
||||
|
||||
/* Lets set the next send date to now so we increment from today, rather than in the past*/
|
||||
if(Carbon::parse($this->next_send_date)->lt(now()->subDays(3)))
|
||||
$this->next_send_date_client = now()->format('Y-m-d');
|
||||
|
||||
}
|
||||
|
||||
switch ($this->frequency_id) {
|
||||
case self::FREQUENCY_DAILY:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addDay();
|
||||
case self::FREQUENCY_WEEKLY:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeek();
|
||||
case self::FREQUENCY_TWO_WEEKS:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(2);
|
||||
case self::FREQUENCY_FOUR_WEEKS:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(4);
|
||||
case self::FREQUENCY_MONTHLY:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthNoOverflow();
|
||||
case self::FREQUENCY_TWO_MONTHS:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(2);
|
||||
case self::FREQUENCY_THREE_MONTHS:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(3);
|
||||
case self::FREQUENCY_FOUR_MONTHS:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(4);
|
||||
case self::FREQUENCY_SIX_MONTHS:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(6);
|
||||
case self::FREQUENCY_ANNUALLY:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYear();
|
||||
case self::FREQUENCY_TWO_YEARS:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(2);
|
||||
case self::FREQUENCY_THREE_YEARS:
|
||||
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(3);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -461,11 +508,11 @@ class RecurringInvoice extends BaseModel
|
||||
|
||||
$data = [];
|
||||
|
||||
if (!Carbon::parse($this->next_send_date)) {
|
||||
if (!Carbon::parse($this->next_send_date_client)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$next_send_date = Carbon::parse($this->next_send_date)->copy();
|
||||
$next_send_date = Carbon::parse($this->next_send_date_client)->copy();
|
||||
|
||||
for ($x=0; $x<$iterations; $x++) {
|
||||
// we don't add the days... we calc the day of the month!!
|
||||
|
@ -106,6 +106,12 @@ class RecurringService
|
||||
$this->stop();
|
||||
}
|
||||
|
||||
if(isset($this->recurring_entity->client))
|
||||
{
|
||||
$offset = $this->recurring_entity->client->timezone_offset();
|
||||
$this->recurring_entity->next_send_date = Carbon::parse($this->recurring_entity->next_send_date_client)->startOfDay()->addSeconds($offset);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,8 @@ class RecurringExpenseTransformer extends EntityTransformer
|
||||
'frequency_id' => (string) $recurring_expense->frequency_id,
|
||||
'remaining_cycles' => (int) $recurring_expense->remaining_cycles,
|
||||
'last_sent_date' => $recurring_expense->last_sent_date ?: '',
|
||||
'next_send_date' => $recurring_expense->next_send_date ?: '',
|
||||
// 'next_send_date' => $recurring_expense->next_send_date ?: '',
|
||||
'next_send_date' => $recurring_expense->next_send_date_client ?: '',
|
||||
'recurring_dates' => (array) [],
|
||||
];
|
||||
|
||||
|
@ -95,7 +95,8 @@ class RecurringInvoiceTransformer extends EntityTransformer
|
||||
'po_number' => $invoice->po_number ?: '',
|
||||
'date' => $invoice->date ?: '',
|
||||
'last_sent_date' => $invoice->last_sent_date ?: '',
|
||||
'next_send_date' => $invoice->next_send_date ?: '',
|
||||
// 'next_send_date' => $invoice->next_send_date ?: '',
|
||||
'next_send_date' => $invoice->next_send_date_client ?: '',
|
||||
'due_date' => $invoice->due_date ?: '',
|
||||
'terms' => $invoice->terms ?: '',
|
||||
'public_notes' => $invoice->public_notes ?: '',
|
||||
|
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class SetRecurringClientTimestamp extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('recurring_invoices', function (Blueprint $table) {
|
||||
$table->datetime('next_send_date_client')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('recurring_expenses', function (Blueprint $table) {
|
||||
$table->datetime('next_send_date_client')->nullable();
|
||||
});
|
||||
|
||||
|
||||
RecurringInvoice::whereNotNull('next_send_date')->cursor()->each(function ($recurring_invoice){
|
||||
$recurring_invoice->next_send_date_client = $recurring_invoice->next_send_date;
|
||||
$recurring_invoice->saveQuietly();
|
||||
});
|
||||
|
||||
RecurringExpense::whereNotNull('next_send_date')->cursor()->each(function ($recurring_expense){
|
||||
$recurring_expense->next_send_date_client = $recurring_expense->next_send_date;
|
||||
$recurring_expense->saveQuietly();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -50,6 +50,46 @@ class RecurringInvoiceTest extends TestCase
|
||||
$this->makeTestData();
|
||||
}
|
||||
|
||||
public function testTimezoneNextSendDateCalculations()
|
||||
{
|
||||
|
||||
$settings = $this->company->settings;
|
||||
$settings->timezone_id = '112';
|
||||
$this->company->settings = $settings;
|
||||
$this->company->save();
|
||||
|
||||
$data = [
|
||||
'frequency_id' => 1,
|
||||
'status_id' => 1,
|
||||
'discount' => 0,
|
||||
'is_amount_discount' => 1,
|
||||
'po_number' => '3434343',
|
||||
'public_notes' => 'notes',
|
||||
'next_send_date' => now()->addDay()->format('Y-m-d'),
|
||||
'is_deleted' => 0,
|
||||
'custom_value1' => 0,
|
||||
'custom_value2' => 0,
|
||||
'custom_value3' => 0,
|
||||
'custom_value4' => 0,
|
||||
'status' => 1,
|
||||
'client_id' => $this->encodePrimaryKey($this->client->id),
|
||||
'line_items' => $this->buildLineItems(),
|
||||
'remaining_cycles' => -1,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/recurring_invoices?start=true', $data)
|
||||
->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(RecurringInvoice::STATUS_ACTIVE, $arr['data']['status_id']);
|
||||
|
||||
$this->assertEquals(now()->addDay()->format('Y-m-d'), $arr['data']['next_send_date']);
|
||||
}
|
||||
|
||||
public function testPostRecurringInvoice()
|
||||
{
|
||||
$data = [
|
||||
|
Loading…
Reference in New Issue
Block a user