diff --git a/src/app/Console/Commands/WalletUntil.php b/src/app/Console/Commands/WalletUntil.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/WalletUntil.php @@ -0,0 +1,50 @@ +argument('wallet')); + + if (!$wallet) { + return 1; + } + + $lastsUntil = $wallet->balanceLastsUntil(); + + $this->info("Lasts until: {$lastsUntil}"); + } +} diff --git a/src/app/Entitlement.php b/src/app/Entitlement.php --- a/src/app/Entitlement.php +++ b/src/app/Entitlement.php @@ -52,6 +52,26 @@ 'cost' => 'integer', ]; + /** + * Return the costs per day for this entitlement. + * + * @return float + */ + public function costsPerDay() + { + if ($this->cost == 0) { + return (float) 0; + } + + $discount = $this->wallet->getDiscountRate(); + + $daysInLastMonth = \App\Utils::daysInLastMonth(); + + $costsPerDay = (float) ($this->cost * $discount) / $daysInLastMonth; + + return $costsPerDay; + } + /** * Create a transaction record for this entitlement. * diff --git a/src/app/Utils.php b/src/app/Utils.php --- a/src/app/Utils.php +++ b/src/app/Utils.php @@ -24,7 +24,7 @@ $start = new Carbon('first day of last month'); $end = new Carbon('last day of last month'); - return $start->diffInDays($end); + return $start->diffInDays($end) + 1; } /** diff --git a/src/app/Wallet.php b/src/app/Wallet.php --- a/src/app/Wallet.php +++ b/src/app/Wallet.php @@ -116,6 +116,30 @@ return $charges; } + /** + * Calculate for how long the current balance will last. + * + * @return \Carbon\Carbon Date + */ + public function balanceLastsUntil() + { + $balance = $this->balance; + + // retrieve any expected charges + $expectedCharge = $this->expectedCharges(); + + // get the costs per day for all entitlements billed against this wallet + $costsPerDay = $this->costsPerDay(); + + // the number of days this balance, minus the expected charges, would last + $daysDelta = ($balance - $expectedCharge) / $costsPerDay; + + // calculate from the last entitlement billed + $entitlement = $this->entitlements()->orderBy('updated_at', 'desc')->first(); + + return $entitlement->updated_at->copy()->addDays($daysDelta); + } + /** * A helper to display human-readable amount of money using * the wallet currency and specified locale. @@ -155,6 +179,22 @@ ); } + /** + * Retrieve the costs per day of everything charged to this wallet. + * + * @return float + */ + public function costsPerDay() + { + $costs = (float) 0; + + foreach ($this->entitlements as $entitlement) { + $costs += $entitlement->costsPerDay(); + } + + return $costs; + } + /** * Add an amount of pecunia to this wallet's balance. * diff --git a/src/tests/Feature/EntitlementTest.php b/src/tests/Feature/EntitlementTest.php --- a/src/tests/Feature/EntitlementTest.php +++ b/src/tests/Feature/EntitlementTest.php @@ -32,6 +32,25 @@ parent::tearDown(); } + public function testCostsPerDay(): void + { + // 444 + // 28 days: 15.86 + // 31 days: 14.32 + $user = $this->getTestUser('entitlement-test@kolabnow.com'); + $package = Package::where('title', 'kolab')->first(); + $mailbox = Sku::where('title', 'mailbox')->first(); + + $user->assignPackage($package); + + $entitlement = $user->entitlements->where('sku_id', $mailbox->id)->first(); + + $costsPerDay = $entitlement->costsPerDay(); + + $this->assertTrue($costsPerDay < 15.86); + $this->assertTrue($costsPerDay > 14.32); + } + /** * Tests for User::AddEntitlement() */ diff --git a/src/tests/Feature/WalletTest.php b/src/tests/Feature/WalletTest.php --- a/src/tests/Feature/WalletTest.php +++ b/src/tests/Feature/WalletTest.php @@ -2,7 +2,9 @@ namespace Tests\Feature; +use App\Package; use App\User; +use App\Sku; use App\Wallet; use Tests\TestCase; use Illuminate\Foundation\Testing\WithFaker; @@ -20,6 +22,7 @@ 'WalletControllerB@WalletController.com', 'WalletController2A@WalletController.com', 'WalletController2B@WalletController.com', + 'jane@kolabnow.com' ]; public function setUp(): void @@ -40,6 +43,44 @@ parent::tearDown(); } + public function testBalanceLastsUntil(): void + { + $user = $this->getTestUser('jane@kolabnow.com'); + $package = Package::where('title', 'kolab')->first(); + $mailbox = Sku::where('title', 'mailbox')->first(); + + $user->assignPackage($package); + + $wallet = $user->wallets()->first(); + + $until = $wallet->balanceLastsUntil(); + + // TODO: Test this for typical cases + // TODO: Test this for a user with no entitlements + // TODO: Test this for a user with 100% discount + $this->markTestIncomplete(); + } + + public function testCostsPerDay(): void + { + // 999 + // 28 days: 35.68 + // 31 days: 32.22 + $user = $this->getTestUser('jane@kolabnow.com'); + + $package = Package::where('title', 'kolab')->first(); + $mailbox = Sku::where('title', 'mailbox')->first(); + + $user->assignPackage($package); + + $wallet = $user->wallets()->first(); + + $costsPerDay = $wallet->costsPerDay(); + + $this->assertTrue($costsPerDay < 35.68); + $this->assertTrue($costsPerDay > 32.22); + } + /** * Verify a wallet is created, when a user is created. */