Page MenuHomePhorge

D2512.1775157680.diff
No OneTemporary

Authored By
Unknown
Size
16 KB
Referenced Files
None
Subscribers
None

D2512.1775157680.diff

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
@@ -113,10 +113,11 @@
$response['skus'] = [];
foreach ($user->entitlements as $ent) {
$sku = $ent->sku;
- $response['skus'][$sku->id] = [
-// 'cost' => $ent->cost,
- 'count' => isset($response['skus'][$sku->id]) ? $response['skus'][$sku->id]['count'] + 1 : 1,
- ];
+ if (!isset($response['skus'][$sku->id])) {
+ $response['skus'][$sku->id] = ['costs' => [], 'count' => 0];
+ }
+ $response['skus'][$sku->id]['count']++;
+ $response['skus'][$sku->id]['costs'][] = $ent->cost;
}
return response()->json($response);
diff --git a/src/resources/js/app.js b/src/resources/js/app.js
--- a/src/resources/js/app.js
+++ b/src/resources/js/app.js
@@ -248,19 +248,15 @@
price(price, currency) {
return ((price || 0) / 100).toLocaleString('de-DE', { style: 'currency', currency: currency || 'CHF' })
},
- priceLabel(cost, units = 1, discount) {
+ priceLabel(cost, discount) {
let index = ''
- if (units < 0) {
- units = 1
- }
-
if (discount) {
cost = Math.floor(cost * ((100 - discount) / 100))
index = '\u00B9'
}
- return this.price(cost * units) + '/month' + index
+ return this.price(cost) + '/month' + index
},
clickRecord(event) {
if (!/^(a|button|svg|path)$/i.test(event.target.nodeName)) {
diff --git a/src/resources/vue/Admin/User.vue b/src/resources/vue/Admin/User.vue
--- a/src/resources/vue/Admin/User.vue
+++ b/src/resources/vue/Admin/User.vue
@@ -454,18 +454,18 @@
.then(response => {
// "merge" SKUs with user entitlement-SKUs
response.data.forEach(sku => {
- if (sku.id in this.user.skus) {
- let count = this.user.skus[sku.id].count
+ const userSku = this.user.skus[sku.id]
+ if (userSku) {
+ let cost = userSku.costs.reduce((sum, current) => sum + current)
let item = {
id: sku.id,
name: sku.name,
- cost: sku.cost,
- units: count - sku.units_free,
- price: this.$root.priceLabel(sku.cost, count - sku.units_free, this.discount)
+ cost: cost,
+ price: this.$root.priceLabel(cost, this.discount)
}
if (sku.range) {
- item.name += ' ' + count + ' ' + sku.range.unit
+ item.name += ' ' + userSku.count + ' ' + sku.range.unit
}
this.skus.push(item)
@@ -587,7 +587,7 @@
this.discount_description = this.wallet.discount_description
this.skus.forEach(sku => {
- sku.price = this.$root.priceLabel(sku.cost, sku.units, this.discount)
+ sku.price = this.$root.priceLabel(sku.cost, this.discount)
})
}
}
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
@@ -88,7 +88,7 @@
<label :for="'pkg-input-' + pkg.id">{{ pkg.name }}</label>
</td>
<td class="price text-nowrap">
- {{ $root.priceLabel(pkg.cost, 1, discount) }}
+ {{ $root.priceLabel(pkg.cost, discount) }}
</td>
<td class="buttons">
<button v-if="pkg.description" type="button" class="btn btn-link btn-lg p-0" v-tooltip.click="pkg.description">
@@ -140,7 +140,7 @@
</div>
</td>
<td class="price text-nowrap">
- {{ $root.priceLabel(sku.cost, 1, discount) }}
+ {{ $root.priceLabel(sku.cost, discount) }}
</td>
<td class="buttons">
<button v-if="sku.description" type="button" class="btn btn-link btn-lg p-0" v-tooltip.click="sku.description">
@@ -254,9 +254,13 @@
// "merge" SKUs with user entitlement-SKUs
this.skus = response.data
.map(sku => {
- if (sku.id in this.user.skus) {
+ const userSku = this.user.skus[sku.id]
+ if (userSku) {
sku.enabled = true
- sku.value = this.user.skus[sku.id].count
+ sku.skuCost = sku.cost
+ sku.cost = userSku.costs.reduce((sum, current) => sum + current)
+ sku.value = userSku.count
+ sku.costs = userSku.costs
} else if (!sku.readonly) {
sku.enabled = false
}
@@ -380,13 +384,28 @@
let record = input.parents('tr').first()
let sku_id = record.find('input[type=checkbox]').val()
let sku = this.findSku(sku_id)
- let cost = sku.cost
+ let existing = sku.costs ? sku.costs.length : 0
+ let cost
+
+ // Calculate cost, considering both existing entitlement cost and sku cost
+ if (existing) {
+ cost = sku.costs
+ .sort((a, b) => a - b) // sort by cost ascending (free units first)
+ .slice(0, value)
+ .reduce((sum, current) => sum + current)
+
+ if (value > existing) {
+ cost += sku.skuCost * (value - existing)
+ }
+ } else {
+ cost = sku.cost * (value - sku.units_free)
+ }
// Update the label
input.prev().text(value + ' ' + sku.range.unit)
// Update the price
- record.find('.price').text(this.$root.priceLabel(cost, value - sku.units_free, this.discount))
+ record.find('.price').text(this.$root.priceLabel(cost, this.discount))
},
findSku(id) {
for (let i = 0; i < this.skus.length; i++) {
diff --git a/src/tests/Browser/Admin/UserTest.php b/src/tests/Browser/Admin/UserTest.php
--- a/src/tests/Browser/Admin/UserTest.php
+++ b/src/tests/Browser/Admin/UserTest.php
@@ -4,6 +4,7 @@
use App\Auth\SecondFactor;
use App\Discount;
+use App\Entitlement;
use App\Sku;
use App\User;
use Tests\Browser;
@@ -37,6 +38,8 @@
$wallet->discount()->dissociate();
$wallet->save();
+ Entitlement::where('cost', '>=', 5000)->delete();
+
$this->clearMeetEntitlements();
}
@@ -57,6 +60,8 @@
$wallet->discount()->dissociate();
$wallet->save();
+ Entitlement::where('cost', '>=', 5000)->delete();
+
$this->clearMeetEntitlements();
parent::tearDown();
@@ -266,6 +271,26 @@
// Now we go to Ned's info page, he's a controller on John's wallet
$this->browse(function (Browser $browser) {
$ned = $this->getTestUser('ned@kolab.org');
+ $beta_sku = Sku::where('title', 'beta')->first();
+ $storage_sku = Sku::where('title', 'storage')->first();
+ $wallet = $ned->wallet();
+
+ // Add an extra storage and beta entitlement with different prices
+ Entitlement::create([
+ 'wallet_id' => $wallet->id,
+ 'sku_id' => $beta_sku->id,
+ 'cost' => 5010,
+ 'entitleable_id' => $ned->id,
+ 'entitleable_type' => User::class
+ ]);
+ Entitlement::create([
+ 'wallet_id' => $wallet->id,
+ 'sku_id' => $storage_sku->id,
+ 'cost' => 5000,
+ 'entitleable_id' => $ned->id,
+ 'entitleable_type' => User::class
+ ]);
+
$page = new UserPage($ned->id);
$browser->click('@user-users tbody tr:nth-child(4) td:first-child a')
@@ -294,20 +319,22 @@
});
// Assert Subscriptions tab, we expect John's discount here
- $browser->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (5)')
+ $browser->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (6)')
->click('@nav #tab-subscriptions')
->with('@user-subscriptions', function (Browser $browser) {
- $browser->assertElementsCount('table tbody tr', 5)
+ $browser->assertElementsCount('table tbody tr', 6)
->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'User Mailbox')
->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '3,99 CHF/month¹')
- ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Storage Quota 2 GB')
- ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '0,00 CHF/month¹')
+ ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Storage Quota 3 GB')
+ ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '45,00 CHF/month¹')
->assertSeeIn('table tbody tr:nth-child(3) td:first-child', 'Groupware Features')
->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '4,99 CHF/month¹')
->assertSeeIn('table tbody tr:nth-child(4) td:first-child', 'Activesync')
->assertSeeIn('table tbody tr:nth-child(4) td:last-child', '0,90 CHF/month¹')
->assertSeeIn('table tbody tr:nth-child(5) td:first-child', '2-Factor Authentication')
->assertSeeIn('table tbody tr:nth-child(5) td:last-child', '0,00 CHF/month¹')
+ ->assertSeeIn('table tbody tr:nth-child(6) td:first-child', 'Private Beta (invitation only)')
+ ->assertSeeIn('table tbody tr:nth-child(6) td:last-child', '45,09 CHF/month¹')
->assertMissing('table tfoot')
->assertSeeIn('table + .hint', '¹ applied discount: 10% - Test voucher')
->assertSeeIn('#reset2fa', 'Reset 2-Factor Auth');
diff --git a/src/tests/Browser/UsersTest.php b/src/tests/Browser/UsersTest.php
--- a/src/tests/Browser/UsersTest.php
+++ b/src/tests/Browser/UsersTest.php
@@ -42,6 +42,7 @@
->where('alias', 'john.test@kolab.org')->delete();
Entitlement::where('entitleable_id', $john->id)->whereIn('cost', [25, 100])->delete();
+ Entitlement::where('cost', '>=', 5000)->delete();
$wallet = $john->wallets()->first();
$wallet->discount()->dissociate();
@@ -64,6 +65,7 @@
->where('alias', 'john.test@kolab.org')->delete();
Entitlement::where('entitleable_id', $john->id)->whereIn('cost', [25, 100])->delete();
+ Entitlement::where('cost', '>=', 5000)->delete();
$wallet = $john->wallets()->first();
$wallet->discount()->dissociate();
@@ -548,7 +550,7 @@
->submitLogon('john@kolab.org', 'simple123', true)
->visit(new UserList())
->waitFor('@table tr:nth-child(2)')
- ->click('@table tr:nth-child(2) a')
+ ->click('@table tr:nth-child(2) a') // joe@kolab.org
->on(new UserInfo())
->with('@form', function (Browser $browser) {
$browser->whenAvailable('@skus', function (Browser $browser) {
@@ -562,7 +564,7 @@
->with($quota_input, function (Browser $browser) {
$browser->setQuotaValue(100);
})
- ->assertSeeIn('tr:nth-child(2) td.price', '21,56 CHF/month¹')
+ ->assertSeeIn('tr:nth-child(2) td.price', '22,05 CHF/month¹')
// groupware SKU
->assertSeeIn('tbody tr:nth-child(3) td.price', '4,99 CHF/month¹')
// ActiveSync SKU
@@ -588,6 +590,51 @@
->assertSeeIn('@packages table + .hint', '¹ applied discount: 10% - Test voucher');
});
});
+
+ // Test using entitlement cost instead of the SKU cost
+ $this->browse(function (Browser $browser) use ($wallet) {
+ $joe = User::where('email', 'joe@kolab.org')->first();
+ $beta_sku = Sku::where('title', 'beta')->first();
+ $storage_sku = Sku::where('title', 'storage')->first();
+
+ // Add an extra storage and beta entitlement with different prices
+ Entitlement::create([
+ 'wallet_id' => $wallet->id,
+ 'sku_id' => $beta_sku->id,
+ 'cost' => 5010,
+ 'entitleable_id' => $joe->id,
+ 'entitleable_type' => User::class
+ ]);
+ Entitlement::create([
+ 'wallet_id' => $wallet->id,
+ 'sku_id' => $storage_sku->id,
+ 'cost' => 5000,
+ 'entitleable_id' => $joe->id,
+ 'entitleable_type' => User::class
+ ]);
+
+ $browser->visit('/user/' . $joe->id)
+ ->on(new UserInfo())
+ ->with('@form', function (Browser $browser) {
+ $browser->whenAvailable('@skus', function (Browser $browser) {
+ $quota_input = new QuotaInput('tbody tr:nth-child(2) .range-input');
+ $browser->waitFor('tbody tr')
+ // Beta SKU
+ ->assertSeeIn('tbody tr:nth-child(7) td.price', '45,09 CHF/month¹')
+ // Storage SKU
+ ->assertSeeIn('tr:nth-child(2) td.price', '45,00 CHF/month¹')
+ ->with($quota_input, function (Browser $browser) {
+ $browser->setQuotaValue(4);
+ })
+ ->assertSeeIn('tr:nth-child(2) td.price', '45,22 CHF/month¹')
+ ->with($quota_input, function (Browser $browser) {
+ $browser->setQuotaValue(2);
+ })
+ ->assertSeeIn('tr:nth-child(2) td.price', '0,00 CHF/month¹');
+ })
+ ->assertSeeIn('@skus table + .hint', '¹ applied discount: 10% - Test voucher');
+ });
+ });
}
/**
diff --git a/src/tests/Feature/Controller/UsersTest.php b/src/tests/Feature/Controller/UsersTest.php
--- a/src/tests/Feature/Controller/UsersTest.php
+++ b/src/tests/Feature/Controller/UsersTest.php
@@ -283,9 +283,13 @@
$this->assertCount(5, $json['skus']);
$this->assertSame(2, $json['skus'][$storage_sku->id]['count']);
+ $this->assertSame([0,0], $json['skus'][$storage_sku->id]['costs']);
$this->assertSame(1, $json['skus'][$groupware_sku->id]['count']);
+ $this->assertSame([555], $json['skus'][$groupware_sku->id]['costs']);
$this->assertSame(1, $json['skus'][$mailbox_sku->id]['count']);
+ $this->assertSame([444], $json['skus'][$mailbox_sku->id]['costs']);
$this->assertSame(1, $json['skus'][$secondfactor_sku->id]['count']);
+ $this->assertSame([0], $json['skus'][$secondfactor_sku->id]['costs']);
}
/**

File Metadata

Mime Type
text/plain
Expires
Thu, Apr 2, 7:21 PM (4 d, 21 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18819917
Default Alt Text
D2512.1775157680.diff (16 KB)

Event Timeline