diff --git a/src/app/Http/Controllers/API/V4/RoomsController.php b/src/app/Http/Controllers/API/V4/RoomsController.php --- a/src/app/Http/Controllers/API/V4/RoomsController.php +++ b/src/app/Http/Controllers/API/V4/RoomsController.php @@ -256,9 +256,7 @@ $room->description = request()->input('description'); $room->save(); - if (!empty($request->skus)) { - SkusController::updateEntitlements($room, $request->skus); - } + SkusController::updateEntitlements($room, $request->skus); if (!$room->hasSKU('group-room')) { $room->setSetting('acl', null); diff --git a/src/app/Http/Controllers/API/V4/SkusController.php b/src/app/Http/Controllers/API/V4/SkusController.php --- a/src/app/Http/Controllers/API/V4/SkusController.php +++ b/src/app/Http/Controllers/API/V4/SkusController.php @@ -131,23 +131,25 @@ return; } - // list of skus, [id=>obj] - $skus = Sku::withEnvTenantContext()->get()->mapWithKeys( + if (!\config('app.with_subscriptions')) { + throw new \Exception("Subscriptions disabled"); + } + + // available SKUs, [id => obj] + $skus = Sku::withObjectTenantContext($object)->get()->mapWithKeys( function ($sku) { return [$sku->id => $sku]; } ); - // existing entitlement's SKUs - $eSkus = []; - - $object->entitlements()->groupBy('sku_id') - ->selectRaw('count(*) as total, sku_id')->each( - function ($e) use (&$eSkus) { - $eSkus[$e->sku_id] = $e->total; - } - ); + // existing object SKUs, [id => total] + $eSkus = $object->entitlements()->groupBy('sku_id')->selectRaw('count(*) as total, sku_id')->get()->mapWithKeys( + function ($e) { + return [$e->sku_id => $e->total]; + } + )->all(); + // compare current and requested state and apply changes (add/remove entitlements) foreach ($skus as $skuID => $sku) { $e = array_key_exists($skuID, $eSkus) ? $eSkus[$skuID] : 0; $r = array_key_exists($skuID, $rSkus) ? $rSkus[$skuID] : 0; diff --git a/src/app/Http/Controllers/API/V4/UsersController.php b/src/app/Http/Controllers/API/V4/UsersController.php --- a/src/app/Http/Controllers/API/V4/UsersController.php +++ b/src/app/Http/Controllers/API/V4/UsersController.php @@ -202,6 +202,7 @@ 'enableResources' => $isController && $hasCustomDomain && $hasBeta, 'enableRooms' => $hasMeet, 'enableSettings' => $isController, + 'enableSubscriptions' => $isController && \config('app.with_subscriptions'), 'enableUsers' => $isController, 'enableWallets' => $isController && \config('app.with_wallet'), 'enableWalletMandates' => $isController, diff --git a/src/config/app.php b/src/config/app.php --- a/src/config/app.php +++ b/src/config/app.php @@ -257,7 +257,6 @@ 'methods_recurring' => env('PAYMENT_METHODS_RECURRING', 'creditcard'), ], - 'with_ldap' => (bool) env('APP_LDAP', true), 'with_imap' => (bool) env('APP_IMAP', false), @@ -265,8 +264,9 @@ 'with_files' => (bool) env('APP_WITH_FILES', false), 'with_reseller' => (bool) env('APP_WITH_RESELLER', false), 'with_services' => (bool) env('APP_WITH_SERVICES', false), - 'with_wallet' => (bool) env('APP_WITH_WALLET', true), 'with_signup' => (bool) env('APP_WITH_SIGNUP', true), + 'with_subscriptions' => (bool) env('APP_WITH_SUBSCRIPTIONS', true), + 'with_wallet' => (bool) env('APP_WITH_WALLET', true), 'signup' => [ 'email_limit' => (int) env('SIGNUP_LIMIT_EMAIL', 0), diff --git a/src/resources/vue/Distlist/Info.vue b/src/resources/vue/Distlist/Info.vue --- a/src/resources/vue/Distlist/Info.vue +++ b/src/resources/vue/Distlist/Info.vue @@ -38,7 +38,7 @@ -
+
diff --git a/src/resources/vue/Domain/Info.vue b/src/resources/vue/Domain/Info.vue --- a/src/resources/vue/Domain/Info.vue +++ b/src/resources/vue/Domain/Info.vue @@ -29,7 +29,7 @@
-
+
diff --git a/src/resources/vue/Resource/Info.vue b/src/resources/vue/Resource/Info.vue --- a/src/resources/vue/Resource/Info.vue +++ b/src/resources/vue/Resource/Info.vue @@ -40,7 +40,7 @@
-
+
diff --git a/src/resources/vue/Room/Info.vue b/src/resources/vue/Room/Info.vue --- a/src/resources/vue/Room/Info.vue +++ b/src/resources/vue/Room/Info.vue @@ -24,7 +24,7 @@ {{ $t('room.description-hint') }}
-
+
diff --git a/src/resources/vue/SharedFolder/Info.vue b/src/resources/vue/SharedFolder/Info.vue --- a/src/resources/vue/SharedFolder/Info.vue +++ b/src/resources/vue/SharedFolder/Info.vue @@ -48,7 +48,7 @@
-
+
diff --git a/src/resources/vue/User/Info.vue b/src/resources/vue/User/Info.vue --- a/src/resources/vue/User/Info.vue +++ b/src/resources/vue/User/Info.vue @@ -81,7 +81,7 @@
-
+
diff --git a/src/tests/Feature/Controller/SkusTest.php b/src/tests/Feature/Controller/SkusTest.php --- a/src/tests/Feature/Controller/SkusTest.php +++ b/src/tests/Feature/Controller/SkusTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature\Controller; +use App\Http\Controllers\API\V4\SkusController; use App\Sku; use App\Tenant; use Tests\TestCase; @@ -100,4 +101,71 @@ $this->assertSame('domain-hosting', $json[0]['title']); $this->assertSame(0, $json[0]['nextCost']); // first domain costs 0 } + + /** + * Test updateEntitlements() method + */ + public function testUpdateEntitlements(): void + { + $jane = $this->getTestUser('jane@kolabnow.com'); + $wallet = $jane->wallets()->first(); + $mailbox_sku = Sku::withEnvTenantContext()->where('title', 'mailbox')->first(); + $storage_sku = Sku::withEnvTenantContext()->where('title', 'storage')->first(); + + // Invalid empty input + SkusController::updateEntitlements($jane, null, $wallet); + + $this->assertSame(0, $wallet->entitlements()->count()); + + // Add mailbox SKU + SkusController::updateEntitlements($jane, [$mailbox_sku->id => 1], $wallet); + + $this->assertSame(1, $wallet->entitlements()->count()); + $this->assertSame($mailbox_sku->id, $wallet->entitlements()->first()->sku_id); + + // Add 2 storage SKUs + $skus = [$mailbox_sku->id => 1, $storage_sku->id => 2]; + SkusController::updateEntitlements($jane, $skus, $wallet); + + $this->assertSame(1, $wallet->entitlements()->where('sku_id', $mailbox_sku->id)->count()); + $this->assertSame(2, $wallet->entitlements()->where('sku_id', $storage_sku->id)->count()); + + // Add two more storage SKUs + $skus = [$mailbox_sku->id => 1, $storage_sku->id => 7]; + SkusController::updateEntitlements($jane, $skus, $wallet); + + $this->assertSame(1, $wallet->entitlements()->where('sku_id', $mailbox_sku->id)->count()); + $this->assertSame(7, $wallet->entitlements()->where('sku_id', $storage_sku->id)->count()); + + // Remove two storage SKUs + $skus = [$mailbox_sku->id => 1, $storage_sku->id => 3]; + SkusController::updateEntitlements($jane, $skus, $wallet); + + $this->assertSame(1, $wallet->entitlements()->where('sku_id', $mailbox_sku->id)->count()); + // Note: 5 not 4 because of free_units=5 + $this->assertSame(5, $wallet->entitlements()->where('sku_id', $storage_sku->id)->count()); + + // Request SKU that can't be assigned to a User object + // Such SKUs are being ignored silently + $group_sku = Sku::withEnvTenantContext()->where('title', 'group')->first(); + $skus = [$mailbox_sku->id => 1, $storage_sku->id => 5, $group_sku->id => 1]; + SkusController::updateEntitlements($jane, $skus, $wallet); + + $this->assertSame(0, $wallet->entitlements()->where('sku_id', $group_sku->id)->count()); + + // Error - add extra mailbox SKU + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Invalid quantity of mailboxes'); + + $skus = [$mailbox_sku->id => 2, $storage_sku->id => 5]; + SkusController::updateEntitlements($jane, $skus, $wallet); + + // Error - disabled subscriptions + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Subscriptions disabled'); + + \config(['app.with_subscriptions' => false]); + $skus = [$mailbox_sku->id => 1]; + SkusController::updateEntitlements($jane, $skus, $wallet); + } }