From 20088f10c43a9be7ab20a68656fc0687273da7f1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 23 Sep 2024 06:33:30 +1000 Subject: [PATCH] Fixes for reminders when calculating across negative GMT timezones. --- app/Services/Invoice/UpdateReminder.php | 28 +++++---- tests/Feature/ReminderTest.php | 78 ++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 17 deletions(-) diff --git a/app/Services/Invoice/UpdateReminder.php b/app/Services/Invoice/UpdateReminder.php index 2a9791ba7ed3..1ff84d0e59e6 100644 --- a/app/Services/Invoice/UpdateReminder.php +++ b/app/Services/Invoice/UpdateReminder.php @@ -47,7 +47,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder1_sent) && $this->settings->schedule_reminder1 == 'after_invoice_date') { - $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays((int)$this->settings->num_days_reminder1); + $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays((int)$this->settings->num_days_reminder1)->addSeconds($offset); if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); @@ -58,7 +58,7 @@ class UpdateReminder extends AbstractService ($this->invoice->partial_due_date || $this->invoice->due_date) && $this->settings->schedule_reminder1 == 'before_due_date') { $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; - $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays((int)$this->settings->num_days_reminder1); + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays((int)$this->settings->num_days_reminder1)->addSeconds($offset); // nlog("1. {$reminder_date->format('Y-m-d')}"); if ($reminder_date->gt(now())) { @@ -71,7 +71,7 @@ class UpdateReminder extends AbstractService $this->settings->schedule_reminder1 == 'after_due_date') { $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; - $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays((int)$this->settings->num_days_reminder1); + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays((int)$this->settings->num_days_reminder1)->addSeconds($offset); // nlog("2. {$reminder_date->format('Y-m-d')}"); if ($reminder_date->gt(now())) { @@ -81,7 +81,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder2_sent) && $this->settings->schedule_reminder2 == 'after_invoice_date') { - $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays((int)$this->settings->num_days_reminder2); + $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays((int)$this->settings->num_days_reminder2)->addSeconds($offset); if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); @@ -93,7 +93,7 @@ class UpdateReminder extends AbstractService $this->settings->schedule_reminder2 == 'before_due_date') { $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; - $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays((int)$this->settings->num_days_reminder2); + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays((int)$this->settings->num_days_reminder2)->addSeconds($offset); // nlog("3. {$reminder_date->format('Y-m-d')}"); if ($reminder_date->gt(now())) { @@ -106,7 +106,7 @@ class UpdateReminder extends AbstractService $this->settings->schedule_reminder2 == 'after_due_date') { $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; - $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays((int)$this->settings->num_days_reminder2); + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays((int)$this->settings->num_days_reminder2)->addSeconds($offset); // nlog("4. {$reminder_date->format('Y-m-d')}"); if ($reminder_date->gt(now())) { @@ -116,7 +116,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder3_sent) && $this->settings->schedule_reminder3 == 'after_invoice_date') { - $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays((int)$this->settings->num_days_reminder3); + $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays((int)$this->settings->num_days_reminder3)->addSeconds($offset); if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); @@ -128,7 +128,7 @@ class UpdateReminder extends AbstractService $this->settings->schedule_reminder3 == 'before_due_date') { $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; - $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays((int)$this->settings->num_days_reminder3); + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays((int)$this->settings->num_days_reminder3)->addSeconds($offset); // nlog("5. {$reminder_date->format('Y-m-d')}"); if ($reminder_date->gt(now())) { @@ -141,7 +141,7 @@ class UpdateReminder extends AbstractService $this->settings->schedule_reminder3 == 'after_due_date') { $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; - $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays((int)$this->settings->num_days_reminder3); + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays((int)$this->settings->num_days_reminder3)->addSeconds($offset); // nlog("6. {$reminder_date->format('Y-m-d')}"); if ($reminder_date->gt(now())) { @@ -154,17 +154,15 @@ class UpdateReminder extends AbstractService ($this->invoice->reminder1_sent || $this->settings->schedule_reminder1 == "" || !$this->settings->enable_reminder1) && ($this->invoice->reminder2_sent || $this->settings->schedule_reminder2 == "" || !$this->settings->enable_reminder2) && ($this->invoice->reminder3_sent || $this->settings->schedule_reminder3 == "" || !$this->settings->enable_reminder3)) { - $reminder_date = $this->addTimeInterval($this->invoice->last_sent_date, (int) $this->settings->endless_reminder_frequency_id); + $reminder_date = $this->addTimeInterval($this->invoice->last_sent_date, (int) $this->settings->endless_reminder_frequency_id)->addSeconds($offset); - if ($reminder_date) { - if ($reminder_date->gt(now())) { - $date_collection->push($reminder_date); - } + if ($reminder_date && $reminder_date->gt(now())) { + $date_collection->push($reminder_date); } } if ($date_collection->count() >= 1 && $date_collection->sort()->first()->gte(now())) { - $this->invoice->next_send_date = $date_collection->sort()->first()->addSeconds($offset); + $this->invoice->next_send_date = $date_collection->sort()->first(); } else { $this->invoice->next_send_date = null; } diff --git a/tests/Feature/ReminderTest.php b/tests/Feature/ReminderTest.php index 65e7eabf35f6..6b98112246ab 100644 --- a/tests/Feature/ReminderTest.php +++ b/tests/Feature/ReminderTest.php @@ -157,6 +157,80 @@ class ReminderTest extends TestCase 'balance' => 10, ]); + } + + public function testReminderScheduleNy() + { + + $settings = CompanySettings::defaults(); + $settings->timezone_id = '15'; + $settings->entity_send_time = 6; + $settings->payment_terms = '14'; + $settings->send_reminders = true; + $settings->enable_reminder1 = true; + $settings->enable_reminder2 = false; + $settings->enable_reminder3 = false; + $settings->enable_reminder_endless = true; + $settings->schedule_reminder1 = 'after_invoice_date'; + $settings->schedule_reminder2 = ''; + $settings->schedule_reminder3 = ''; + $settings->num_days_reminder1 = 1; + $settings->num_days_reminder2 = 0; + $settings->num_days_reminder3 = 0; + $settings->endless_reminder_frequency_id = '1'; + + $this->buildData($settings); + + $this->travelTo(Carbon::parse('2024-09-20')->startOfDay()->addHours(1)); + + $invoice = Invoice::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'amount' => 10, + 'balance' => 10, + 'date' => '2024-09-19', + 'number' => 'JJJ1-11-2024', + 'due_date' => '2024-09-19', + 'status_id' => 2, + 'last_sent_date' => '19-09-2024', + ]); + + $this->assertEquals(1, $invoice->company->settings->num_days_reminder1); + + $invoice->service()->setReminder($settings)->save(); + + $this->assertEquals(10, $invoice->balance); + $this->assertEquals('2024-09-20', $invoice->next_send_date->format('Y-m-d')); + + + $x = false; + do { + + $this->travelTo(now()->addHour()); + (new ReminderJob())->handle(); + $invoice = $invoice->fresh(); + + $x = (bool)$invoice->reminder1_sent; + } while ($x === false); + + $this->assertNotNull($invoice->reminder_last_sent); + $this->assertEquals(now()->addDays(1), $invoice->next_send_date); + + $x = 0; + do { + + $this->travelTo(now()->addHour()); + (new ReminderJob())->handle(); + $invoice = $invoice->fresh(); + + $x++; + } while ($x < 24); + + $this->assertEquals(now()->addDays(1), $invoice->next_send_date); + + + } public function testDKRemindersNotSending() @@ -434,7 +508,7 @@ class ReminderTest extends TestCase $fee = collect($this->invoice->line_items)->where('cost', 102)->first(); $this->assertEquals(102, $fee->cost); - $this->assertEquals('Fee added '.now()->format('d/M/Y'), $fee->notes); + $this->assertEquals('Late fee added on '.now()->format('d/M/Y'), $fee->notes); $this->travelTo(now()->addDay()->startOfDay()->addHour()); @@ -446,7 +520,7 @@ class ReminderTest extends TestCase $fee = collect($this->invoice->line_items)->where('cost', 103)->first(); $this->assertEquals(103, $fee->cost); - $this->assertEquals('Fee added '.now()->format('d/M/Y'), $fee->notes); + $this->assertEquals('Late fee added on '.now()->format('d/M/Y'), $fee->notes); $this->travelBack();