diff --git a/src/app/Console/Commands/Wallet/TrialEndCommand.php b/src/app/Console/Commands/Wallet/TrialEndCommand.php --- a/src/app/Console/Commands/Wallet/TrialEndCommand.php +++ b/src/app/Console/Commands/Wallet/TrialEndCommand.php @@ -27,19 +27,30 @@ */ public function handle() { - // Get all wallets, excluding deleted/inactive accounts - // created precisely a month ago + // Get all wallets... $wallets = \App\Wallet::select('wallets.*') ->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) { diff --git a/src/tests/Feature/Console/Wallet/TrialEndTest.php b/src/tests/Feature/Console/Wallet/TrialEndTest.php --- a/src/tests/Feature/Console/Wallet/TrialEndTest.php +++ b/src/tests/Feature/Console/Wallet/TrialEndTest.php @@ -16,7 +16,8 @@ { parent::setUp(); - $this->deleteTestUser('wallets-controller@kolabnow.com'); + $this->deleteTestUser('test-user1@kolabnow.com'); + $this->deleteTestUser('test-user22@kolabnow.com'); } /** @@ -24,7 +25,8 @@ */ public function tearDown(): void { - $this->deleteTestUser('wallets-controller@kolabnow.com'); + $this->deleteTestUser('test-user1@kolabnow.com'); + $this->deleteTestUser('test-user22@kolabnow.com'); parent::tearDown(); } @@ -36,20 +38,30 @@ { 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(); @@ -87,5 +99,17 @@ 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(); } }