diff --git a/src/app/Console/Commands/Wallet/TrialEndCommand.php b/src/app/Console/Commands/Wallet/TrialEndCommand.php index 79c85821..b05847f6 100644 --- a/src/app/Console/Commands/Wallet/TrialEndCommand.php +++ b/src/app/Console/Commands/Wallet/TrialEndCommand.php @@ -1,53 +1,64 @@ join('users', 'users.id', '=', 'wallets.user_id') - ->leftJoin('wallet_settings', function ($join) { - $join->on('wallet_settings.wallet_id', '=', 'wallets.id') - ->where('wallet_settings.key', 'trial_end_notice'); - }) ->withEnvTenantContext('users') + // exclude deleted accounts ->whereNull('users.deleted_at') + // exclude "inactive" accounts ->where('users.status', '&', \App\User::STATUS_IMAP_READY) + // consider only these created 1 to 2 months ago ->where('users.created_at', '>', \now()->subMonthsNoOverflow(2)) - ->whereNull('wallet_settings.value') + ->where('users.created_at', '<=', \now()->subMonthsNoOverflow(1)) + // skip wallets with the notification already sent + ->whereNotExists(function ($query) { + $query->from('wallet_settings') + ->where('wallet_settings.key', 'trial_end_notice') + ->whereColumn('wallet_settings.wallet_id', 'wallets.id'); + }) + // skip users that aren't account owners + ->whereExists(function ($query) { + $query->from('entitlements') + ->where('entitlements.entitleable_type', \App\User::class) + ->whereColumn('entitlements.entitleable_id', 'wallets.user_id') + ->whereColumn('entitlements.wallet_id', 'wallets.id'); + }) ->cursor(); foreach ($wallets as $wallet) { // Send the email asynchronously \App\Jobs\TrialEndEmail::dispatch($wallet->owner); // Store the timestamp $wallet->setSetting('trial_end_notice', (string) \now()); } } } diff --git a/src/tests/Feature/Console/Wallet/TrialEndTest.php b/src/tests/Feature/Console/Wallet/TrialEndTest.php index 329ed38d..e7d9381b 100644 --- a/src/tests/Feature/Console/Wallet/TrialEndTest.php +++ b/src/tests/Feature/Console/Wallet/TrialEndTest.php @@ -1,91 +1,115 @@ deleteTestUser('wallets-controller@kolabnow.com'); + $this->deleteTestUser('test-user1@kolabnow.com'); + $this->deleteTestUser('test-user22@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { - $this->deleteTestUser('wallets-controller@kolabnow.com'); + $this->deleteTestUser('test-user1@kolabnow.com'); + $this->deleteTestUser('test-user22@kolabnow.com'); parent::tearDown(); } /** * Test command run */ public function testHandle(): void { Queue::fake(); - $user = $this->getTestUser('wallets-controller@kolabnow.com', [ + $package = \App\Package::withEnvTenantContext()->where('title', 'lite')->first(); + $user = $this->getTestUser('test-user1@kolabnow.com', [ 'status' => User::STATUS_IMAP_READY | User::STATUS_LDAP_READY | User::STATUS_ACTIVE, ]); $wallet = $user->wallets()->first(); + $user->assignPackage($package); DB::table('users')->update(['created_at' => \now()->clone()->subMonthsNoOverflow(2)->subHours(1)]); - // Expect no wallets in after-trial state + // No wallets in after-trial state, no email sent + Queue::fake(); + $code = \Artisan::call("wallet:trial-end"); + Queue::assertNothingPushed(); + + // Expect no email sent (out of time boundaries) + $user->created_at = \now()->clone()->subMonthsNoOverflow(1)->addHour(); + $user->save(); + Queue::fake(); $code = \Artisan::call("wallet:trial-end"); Queue::assertNothingPushed(); // Test an email sent - $user->created_at = \now()->clone()->subMonthNoOverflow(); + $user->created_at = \now()->clone()->subMonthsNoOverflow(1); $user->save(); Queue::fake(); $code = \Artisan::call("wallet:trial-end"); Queue::assertPushed(\App\Jobs\TrialEndEmail::class, 1); Queue::assertPushed(\App\Jobs\TrialEndEmail::class, function ($job) use ($user) { $job_user = TestCase::getObjectProperty($job, 'account'); return $job_user->id === $user->id; }); $dt = $wallet->getSetting('trial_end_notice'); $this->assertMatchesRegularExpression('/^' . date('Y-m-d') . ' [0-9]{2}:[0-9]{2}:[0-9]{2}$/', $dt); // Test no duplicate email sent for the same wallet Queue::fake(); $code = \Artisan::call("wallet:trial-end"); Queue::assertNothingPushed(); // Test not imap ready user - no email sent $wallet->setSetting('trial_end_notice', null); $user->status = User::STATUS_NEW | User::STATUS_LDAP_READY | User::STATUS_ACTIVE; $user->save(); Queue::fake(); $code = \Artisan::call("wallet:trial-end"); Queue::assertNothingPushed(); // Test deleted user - no email sent $user->status = User::STATUS_NEW | User::STATUS_LDAP_READY | User::STATUS_ACTIVE | User::STATUS_IMAP_READY; $user->save(); $user->delete(); Queue::fake(); $code = \Artisan::call("wallet:trial-end"); Queue::assertNothingPushed(); $this->assertNull($wallet->getSetting('trial_end_notice')); + + // Make sure the non-controller users are omitted + $user2 = $this->getTestUser('test-user2@kolabnow.com', [ + 'status' => User::STATUS_IMAP_READY | User::STATUS_LDAP_READY | User::STATUS_ACTIVE, + ]); + $user->assignPackage($package, $user2); + $user2->created_at = \now()->clone()->subMonthsNoOverflow(1); + $user2->save(); + + Queue::fake(); + $code = \Artisan::call("wallet:trial-end"); + Queue::assertNothingPushed(); } }