diff --git a/app/Jobs/Cron/SubscriptionCron.php b/app/Jobs/Cron/SubscriptionCron.php index 6e29ba088384..a3a5286a3816 100644 --- a/app/Jobs/Cron/SubscriptionCron.php +++ b/app/Jobs/Cron/SubscriptionCron.php @@ -39,62 +39,25 @@ class SubscriptionCron */ public function handle(): void { - nlog('Subscription Cron'); Auth::logout(); if (! config('ninja.db.multi_db_enabled')) { - $invoices = Invoice::where('is_deleted', 0) - ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) - ->where('balance', '>', 0) - ->where('is_proforma', 0) - ->whereDate('due_date', '<=', now()->addDay()->startOfDay()) - ->whereNull('deleted_at') - ->whereNotNull('subscription_id') - ->cursor(); - $invoices->each(function (Invoice $invoice) { - $subscription = $invoice->subscription; + nlog('Subscription Cron '. now()->toDateTimeString()); - $body = [ - 'context' => 'plan_expired', - 'client' => $invoice->client->hashed_id, - 'invoice' => $invoice->hashed_id, - 'subscription' => $subscription->hashed_id, - ]; + $this->timezoneAware(); - $this->sendLoad($subscription, $body); - //This will send the notification daily. - //We'll need to handle this by performing some action on the invoice to either archive it or delete it? - }); + } else { //multiDB environment, need to foreach (MultiDB::$dbs as $db) { MultiDB::setDB($db); - $invoices = Invoice::where('is_deleted', 0) - ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) - ->where('balance', '>', 0) - ->where('is_proforma', 0) - ->whereDate('due_date', '<=', now()->addDay()->startOfDay()) - ->whereNull('deleted_at') - ->whereNotNull('subscription_id') - ->cursor(); + nlog('Subscription Cron for ' . $db . ' ' . now()->toDateTimeString()); - $invoices->each(function (Invoice $invoice) { - $subscription = $invoice->subscription; + $this->timezoneAware(); - $body = [ - 'context' => 'plan_expired', - 'client' => $invoice->client->hashed_id, - 'invoice' => $invoice->hashed_id, - 'subscription' => $subscription->hashed_id, - ]; - - $this->sendLoad($subscription, $body); - //This will send the notification daily. - //We'll need to handle this by performing some action on the invoice to either archive it or delete it? - }); } } } @@ -131,7 +94,7 @@ class SubscriptionCron ->where('is_proforma', 0) ->whereNotNull('subscription_id') ->where('balance', '>', 0) - ->whereDate('due_date', '<=', now()->setTimezone($company->timezone()->name)->addDay()->startOfDay()) + ->whereDate('due_date', '<=', now()->addDay()->startOfDay()) ->cursor() ->each(function (Invoice $invoice) { diff --git a/tests/Feature/SubscriptionApiTest.php b/tests/Feature/SubscriptionApiTest.php index a0fb674d794a..f7b1bee1af20 100644 --- a/tests/Feature/SubscriptionApiTest.php +++ b/tests/Feature/SubscriptionApiTest.php @@ -12,13 +12,19 @@ namespace Tests\Feature; use Tests\TestCase; +use App\Models\User; +use App\Models\Client; +use App\Models\Company; use App\Models\Invoice; use App\Models\Product; -use App\Models\RecurringInvoice; use Tests\MockAccountData; use Illuminate\Support\Str; +use App\Models\CompanyToken; use App\Models\Subscription; use App\Utils\Traits\MakesHash; +use App\Models\RecurringInvoice; +use App\DataMapper\CompanySettings; +use App\Factory\CompanyUserFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Session; use Illuminate\Validation\ValidationException; @@ -51,6 +57,250 @@ class SubscriptionApiTest extends TestCase Model::reguard(); } + public function testSubscriptionCronLocalization() + { + + $settings = CompanySettings::defaults(); + $settings->timezone_id = '50'; //europe/vienna + + $c2 = Company::factory()->create([ + 'account_id' => $this->company->account_id, + 'settings' => $settings + ]); + + $cu = CompanyUserFactory::create($this->user->id, $c2->id, $this->account->id); + $cu->is_owner = true; + $cu->is_admin = true; + $cu->is_locked = true; + $cu->permissions = '["view_client"]'; + $cu->save(); + + $different_company_token = \Illuminate\Support\Str::random(64); + + $company_token = new CompanyToken(); + $company_token->user_id = $this->user->id; + $company_token->company_id = $c2->id; + $company_token->account_id = $c2->account_id; + $company_token->name = 'test token'; + $company_token->token = $different_company_token; + $company_token->is_system = true; + $company_token->save(); + + + $s = Subscription::factory()->create([ + 'company_id' => $c2->id, + 'user_id' => $this->user->id, + ]); + + $client2 = Client::factory()->create([ + 'company_id' => $c2->id, + 'user_id' => $this->user->id, + ]); + + $i = Invoice::factory()->create([ + 'company_id' => $c2->id, + 'user_id' => $this->user->id, + 'subscription_id' => $s->id, + 'due_date' => now()->startOfDay(), + 'client_id' => $client2->id, + 'status_id' => Invoice::STATUS_SENT + ]); + + $settings = CompanySettings::defaults(); + $settings->timezone_id = '110'; //sydney/australia + + $c = Company::factory()->create([ + 'account_id' => $this->company->account_id, + 'settings' => $settings, + ]); + + $cu = CompanyUserFactory::create($this->user->id, $c->id, $this->account->id); + $cu->is_owner = true; + $cu->is_admin = true; + $cu->is_locked = true; + $cu->permissions = '["view_client"]'; + $cu->save(); + + $different_company_token = \Illuminate\Support\Str::random(64); + + $company_token = new CompanyToken(); + $company_token->user_id = $this->user->id; + $company_token->company_id = $c->id; + $company_token->account_id = $c->account_id; + $company_token->name = 'test token'; + $company_token->token = $different_company_token; + $company_token->is_system = true; + $company_token->save(); + + $s1 = Subscription::factory()->create([ + 'company_id' => $c->id, + 'user_id' => $this->user->id, + ]); + + + $client = Client::factory()->create([ + 'company_id' => $c2->id, + 'user_id' => $this->user->id, + ]); + + $i = Invoice::factory()->create([ + 'company_id' => $c->id, + 'user_id' => $this->user->id, + 'subscription_id' => $s1->id, + 'due_date' => now()->startOfDay(), + 'client_id' => $client->id, + 'status_id' => Invoice::STATUS_SENT + ]); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + $company = Company::find($c->id); //sydney + + $timezone_now = now()->setTimezone($company->timezone()->name); + + $this->assertEquals('Australia/Sydney', $timezone_now->timezoneName); + + $this->travelTo($timezone_now->copy()->startOfDay()->subHour()); + + $i = false; + + //Capture companies within the window of 00:00 and 00:30 + if($timezone_now->gte($timezone_now->copy()->startOfDay()) && $timezone_now->lt($timezone_now->copy()->startOfDay()->addMinutes(30))) { + + $i = Invoice::query() + ->where('company_id', $company->id) + ->whereNull('deleted_at') + ->where('is_deleted', 0) + ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('is_proforma', 0) + ->whereNotNull('subscription_id') + ->where('balance', '>', 0) + ->whereDate('due_date', '<=', now()->setTimezone($company->timezone()->name)->addDay()->startOfDay()) + ->get(); + + } + + $this->assertFalse($i); + + $this->travelTo($timezone_now->copy()->startOfDay()); + + if(now()->gte($timezone_now->copy()->startOfDay()) && now()->lt($timezone_now->copy()->startOfDay()->addMinutes(30))) { + + $i = Invoice::query() + ->where('company_id', $company->id) + ->whereNull('deleted_at') + ->where('is_deleted', 0) + ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('is_proforma', 0) + ->whereNotNull('subscription_id') + ->whereDate('due_date', '<=', now()->setTimezone($company->timezone()->name)->addDay()->startOfDay()) + ->get(); + + } + + $this->assertEquals(1, $i->count()); + + $i = false; + + $this->travelTo($timezone_now->copy()->startOfDay()->addHours(2)); + + if($timezone_now->gte($timezone_now->copy()->startOfDay()) && $timezone_now->lt($timezone_now->copy()->startOfDay()->addMinutes(30))) { + + $i = Invoice::query() + ->where('company_id', $company->id) + ->whereNull('deleted_at') + ->where('is_deleted', 0) + ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('is_proforma', 0) + ->whereNotNull('subscription_id') + ->where('balance', '>', 0) + ->whereDate('due_date', '<=', now()->setTimezone($company->timezone()->name)->addDay()->startOfDay()) + ->get(); + + } + + $this->assertFalse($i); + + $count = Invoice::whereNotNull('subscription_id')->count(); + + $this->assertEquals(2, $count); + + $this->travelBack(); + //////////////////////////////////////////// vienna ////////////////////////////////////////////////// + + $company = Company::find($c2->id); //vienna + + $timezone_now = now()->setTimezone($company->timezone()->name); + + $this->assertEquals('Europe/Vienna', $timezone_now->timezoneName); + + $this->travelTo($timezone_now->copy()->startOfDay()->subHour()); + + $this->travelTo($timezone_now->copy()->startOfDay()->subHour()); + + $i = false; + + //Capture companies within the window of 00:00 and 00:30 + if($timezone_now->gte($timezone_now->copy()->startOfDay()) && $timezone_now->lt($timezone_now->copy()->startOfDay()->addMinutes(30))) { + + $i = Invoice::query() + ->where('company_id', $company->id) + ->whereNull('deleted_at') + ->where('is_deleted', 0) + ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('is_proforma', 0) + ->whereNotNull('subscription_id') + ->where('balance', '>', 0) + ->whereDate('due_date', '<=', now()->setTimezone($company->timezone()->name)->addDay()->startOfDay()) + ->get(); + + } + + $this->assertFalse($i); + + $this->travelTo($timezone_now->copy()->startOfDay()); + + if(now()->gte($timezone_now->copy()->startOfDay()) && now()->lt($timezone_now->copy()->startOfDay()->addMinutes(30))) { + + $i = Invoice::query() + ->where('company_id', $company->id) + ->whereNull('deleted_at') + ->where('is_deleted', 0) + ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('is_proforma', 0) + ->whereNotNull('subscription_id') + ->whereDate('due_date', '<=', now()->setTimezone($company->timezone()->name)->addDay()->startOfDay()) + ->get(); + + } + + $this->assertEquals(1, $i->count()); + + $i = false; + + $this->travelTo($timezone_now->copy()->startOfDay()->addHours(2)); + + if($timezone_now->gte($timezone_now->copy()->startOfDay()) && $timezone_now->lt($timezone_now->copy()->startOfDay()->addMinutes(30))) { + + $i = Invoice::query() + ->where('company_id', $company->id) + ->whereNull('deleted_at') + ->where('is_deleted', 0) + ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('is_proforma', 0) + ->whereNotNull('subscription_id') + ->where('balance', '>', 0) + ->whereDate('due_date', '<=', now()->setTimezone($company->timezone()->name)->addDay()->startOfDay()) + ->get(); + + } + + $this->assertFalse($i); + + + + } + public function testAssignInvoice() { $i = Invoice::factory()