Page MenuHomePhorge

D1063.1775375264.diff
No OneTemporary

Authored By
Unknown
Size
31 KB
Referenced Files
None
Subscribers
None

D1063.1775375264.diff

diff --git a/src/app/Console/Commands/DiscountList.php b/src/app/Console/Commands/DiscountList.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/DiscountList.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Discount;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+
+class DiscountList extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'discount:list';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'List available (active) discounts';
+
+ /**
+ * Create a new command instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ Discount::where('active', true)->orderBy('discount')->get()->each(function ($discount) {
+ $name = $discount->description;
+
+ if ($discount->code) {
+ $name .= " [{$discount->code}]";
+ }
+
+ $this->info("{$discount->discount}%: {$name} ({$discount->id})");
+ });
+ }
+}
diff --git a/src/app/Console/Commands/UserDiscount.php b/src/app/Console/Commands/UserDiscount.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/UserDiscount.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+
+class UserDiscount extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'user:discount {user} {discount}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = "Apply a discount to all of the user's wallets";
+
+ /**
+ * Create a new command instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $user = \App\User::where('email', $this->argument('user'))->first();
+
+ if (!$user) {
+ return 1;
+ }
+
+ $this->info("Found user {$user->id}");
+
+ if ($this->argument('discount') === '0') {
+ $discount = null;
+ } else {
+ $discount = \App\Discount::find($this->argument('discount'));
+
+ if (!$discount) {
+ return 1;
+ }
+ }
+
+ foreach ($user->wallets as $wallet) {
+ if (!$discount) {
+ $wallet->discount()->dissociate();
+ } else {
+ $wallet->discount()->associate($discount);
+ }
+
+ $wallet->save();
+ }
+ }
+}
diff --git a/src/app/Console/Commands/UserWallets.php b/src/app/Console/Commands/UserWallets.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/UserWallets.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+
+class UserWallets extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'user:wallets {user}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'List wallets for a user';
+
+ /**
+ * Create a new command instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $user = \App\User::where('email', $this->argument('user'))->first();
+
+ if (!$user) {
+ return 1;
+ }
+
+ foreach ($user->wallets as $wallet) {
+ $this->info("{$wallet->id} {$wallet->description}");
+ }
+ }
+}
diff --git a/src/app/Console/Commands/WalletDiscount.php b/src/app/Console/Commands/WalletDiscount.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/WalletDiscount.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+
+class WalletDiscount extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'wallet:discount {wallet} {discount}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Apply a discount to a wallet';
+
+ /**
+ * Create a new command instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $wallet = \App\Wallet::where('id', $this->argument('wallet'))->first();
+
+ if (!$wallet) {
+ return 1;
+ }
+
+ // FIXME: Using '0' for delete might be not that obvious
+
+ if ($this->argument('discount') === '0') {
+ $wallet->discount()->dissociate();
+ } else {
+ $discount = \App\Discount::find($this->argument('discount'));
+
+ if (!$discount) {
+ return 1;
+ }
+
+ $wallet->discount()->associate($discount);
+ }
+
+ $wallet->save();
+ }
+}
diff --git a/src/app/Discount.php b/src/app/Discount.php
new file mode 100644
--- /dev/null
+++ b/src/app/Discount.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+use Spatie\Translatable\HasTranslations;
+
+/**
+ * The eloquent definition of a Discount.
+ */
+class Discount extends Model
+{
+ use HasTranslations;
+
+ public $incrementing = false;
+ protected $keyType = 'string';
+
+ protected $casts = [
+ 'discount' => 'integer',
+ ];
+
+ protected $fillable = [
+ 'active',
+ 'code',
+ 'description',
+ 'discount',
+ ];
+
+ /** @var array Translatable properties */
+ public $translatable = [
+ 'description',
+ ];
+
+ /**
+ * Discount value mutator
+ *
+ * @throws \Exception
+ */
+ public function setDiscountAttribute($discount)
+ {
+ $discount = (int) $discount;
+
+ if ($discount < 0 || $discount > 100) {
+ throw new \Exception("Invalid discount value, expected integer in range of 0-100");
+ }
+
+ $this->attributes['discount'] = $discount;
+ }
+
+ /**
+ * List of wallets with this discount assigned.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\HasMany
+ */
+ public function wallets()
+ {
+ return $this->hasMany('App\Wallet');
+ }
+}
diff --git a/src/app/Http/Controllers/API/UsersController.php b/src/app/Http/Controllers/API/UsersController.php
--- a/src/app/Http/Controllers/API/UsersController.php
+++ b/src/app/Http/Controllers/API/UsersController.php
@@ -481,10 +481,22 @@
$response = array_merge($response, self::userStatuses($user));
+ // Add discount info to wallet object output
+ $map_func = function ($wallet) {
+ $result = $wallet->toArray();
+
+ if ($wallet->discount) {
+ $result['discount'] = $wallet->discount->discount;
+ $result['discount_description'] = $wallet->discount->description;
+ }
+
+ return $result;
+ };
+
// Information about wallets and accounts for access checks
- $response['wallets'] = $user->wallets->toArray();
- $response['accounts'] = $user->accounts->toArray();
- $response['wallet'] = $user->wallet()->toArray();
+ $response['wallets'] = $user->wallets->map($map_func)->toArray();
+ $response['accounts'] = $user->accounts->map($map_func)->toArray();
+ $response['wallet'] = $map_func($user->wallet());
return $response;
}
diff --git a/src/app/Observers/DiscountObserver.php b/src/app/Observers/DiscountObserver.php
new file mode 100644
--- /dev/null
+++ b/src/app/Observers/DiscountObserver.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Observers;
+
+use App\Discount;
+
+/**
+ * This is an observer for the Discount model definition.
+ */
+class DiscountObserver
+{
+ /**
+ * Ensure the discount ID is a custom ID (uuid).
+ *
+ * @param \App\Discount $discount The discount object
+ *
+ * @return void
+ */
+ public function creating(Discount $discount)
+ {
+ while (true) {
+ $allegedly_unique = \App\Utils::uuidStr();
+ if (!Discount::find($allegedly_unique)) {
+ $discount->{$discount->getKeyName()} = $allegedly_unique;
+ break;
+ }
+ }
+ }
+}
diff --git a/src/app/Providers/AppServiceProvider.php b/src/app/Providers/AppServiceProvider.php
--- a/src/app/Providers/AppServiceProvider.php
+++ b/src/app/Providers/AppServiceProvider.php
@@ -24,6 +24,7 @@
*/
public function boot()
{
+ \App\Discount::observe(\App\Observers\DiscountObserver::class);
\App\Domain::observe(\App\Observers\DomainObserver::class);
\App\Entitlement::observe(\App\Observers\EntitlementObserver::class);
\App\Package::observe(\App\Observers\PackageObserver::class);
diff --git a/src/app/Wallet.php b/src/app/Wallet.php
--- a/src/app/Wallet.php
+++ b/src/app/Wallet.php
@@ -33,7 +33,7 @@
];
protected $nullable = [
- 'description'
+ 'description',
];
protected $casts = [
@@ -59,6 +59,8 @@
public function chargeEntitlements($apply = true)
{
$charges = 0;
+ $discount = $this->discount ? $this->discount->discount : 0;
+ $discount = (100 - $discount) / 100;
foreach ($this->entitlements()->get()->fresh() as $entitlement) {
// This entitlement has been created less than or equal to 14 days ago (this is at
@@ -76,7 +78,9 @@
if ($entitlement->updated_at <= Carbon::now()->subMonthsWithoutOverflow(1)) {
$diff = $entitlement->updated_at->diffInMonths(Carbon::now());
- $charges += $entitlement->cost * $diff;
+ $cost = (int) ($entitlement->cost * $discount * $diff);
+
+ $charges += $cost;
// if we're in dry-run, you know...
if (!$apply) {
@@ -86,13 +90,25 @@
$entitlement->updated_at = $entitlement->updated_at->copy()->addMonthsWithoutOverflow($diff);
$entitlement->save();
- $this->debit($entitlement->cost * $diff);
+ // TODO: This would be better done out of the loop (debit() will call save()),
+ // but then, maybe we should use a db transaction
+ $this->debit($cost);
}
}
return $charges;
}
+ /**
+ * The discount assigned to the wallet.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ */
+ public function discount()
+ {
+ return $this->belongsTo('App\Discount', 'discount_id', 'id');
+ }
+
/**
* Calculate the expected charges to this wallet.
*
diff --git a/src/database/migrations/2020_03_30_100000_create_discounts.php b/src/database/migrations/2020_03_30_100000_create_discounts.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2020_03_30_100000_create_discounts.php
@@ -0,0 +1,59 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+// phpcs:ignore
+class CreateDiscounts extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::create(
+ 'discounts',
+ function (Blueprint $table) {
+ $table->string('id', 36);
+ $table->tinyInteger('discount')->unsigned();
+ $table->json('description');
+ $table->string('code', 32)->nullable();
+ $table->boolean('active')->default(false);
+ $table->timestamps();
+
+ $table->primary('id');
+ }
+ );
+
+ Schema::table(
+ 'wallets',
+ function (Blueprint $table) {
+ $table->string('discount_id', 36)->nullable();
+
+ $table->foreign('discount_id')->references('id')
+ ->on('discounts')->onDelete('set null');
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table(
+ 'wallets',
+ function (Blueprint $table) {
+ $table->dropForeign(['discount_id']);
+ $table->dropColumn('discount_id');
+ }
+ );
+
+ Schema::dropIfExists('discounts');
+ }
+}
diff --git a/src/database/seeds/DatabaseSeeder.php b/src/database/seeds/DatabaseSeeder.php
--- a/src/database/seeds/DatabaseSeeder.php
+++ b/src/database/seeds/DatabaseSeeder.php
@@ -13,6 +13,7 @@
{
$this->call(
[
+ DiscountSeeder::class,
DomainSeeder::class,
SkuSeeder::class,
PackageSeeder::class,
diff --git a/src/database/seeds/DiscountSeeder.php b/src/database/seeds/DiscountSeeder.php
new file mode 100644
--- /dev/null
+++ b/src/database/seeds/DiscountSeeder.php
@@ -0,0 +1,40 @@
+<?php
+
+use App\Discount;
+use Illuminate\Database\Seeder;
+
+class DiscountSeeder extends Seeder
+{
+ /**
+ * Run the database seeds.
+ *
+ * @return void
+ */
+ public function run()
+ {
+ Discount::create(
+ [
+ 'description' => 'Free Account',
+ 'discount' => 100,
+ 'active' => true,
+ ]
+ );
+
+ Discount::create(
+ [
+ 'description' => 'Student or Educational Institution',
+ 'discount' => 30,
+ 'active' => true,
+ ]
+ );
+
+ Discount::create(
+ [
+ 'description' => 'Test voucher',
+ 'discount' => 10,
+ 'active' => true,
+ 'code' => 'TEST',
+ ]
+ );
+ }
+}
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
@@ -69,7 +69,7 @@
{{ pkg.name }}
</td>
<td class="price text-nowrap">
- {{ $root.price(pkg.cost) + '/month' }}
+ {{ price(pkg.cost) }}
</td>
<td class="buttons">
<button v-if="pkg.description" type="button" class="btn btn-link btn-lg p-0" v-tooltip.click="pkg.description">
@@ -80,6 +80,10 @@
</tr>
</tbody>
</table>
+ <small v-if="discount > 0" class="hint">
+ <hr class="m-0">
+ &sup1; applied discount: {{ discount }}% - {{ discount_description }}
+ </small>
</div>
</div>
<div v-if="user_id !== 'new'" id="user-skus" class="form-group row">
@@ -117,7 +121,7 @@
</div>
</td>
<td class="price text-nowrap">
- {{ $root.price(sku.cost) + '/month' }}
+ {{ price(sku.cost) }}
</td>
<td class="buttons">
<button v-if="sku.description" type="button" class="btn btn-link btn-lg p-0" v-tooltip.click="sku.description">
@@ -128,6 +132,10 @@
</tr>
</tbody>
</table>
+ <small v-if="discount > 0" class="hint">
+ <hr class="m-0">
+ &sup1; applied discount: {{ discount }}% - {{ discount_description }}
+ </small>
</div>
</div>
<button class="btn btn-primary" type="submit"><svg-icon icon="check"></svg-icon> Submit</button>
@@ -142,6 +150,8 @@
export default {
data() {
return {
+ discount: 0,
+ discount_description: '',
user_id: null,
user: {},
packages: [],
@@ -152,6 +162,17 @@
created() {
this.user_id = this.$route.params.user
+ let wallet = this.$store.state.authInfo.accounts[0]
+
+ if (!wallet) {
+ wallet = this.$store.state.authInfo.wallets[0]
+ }
+
+ if (wallet && wallet.discount) {
+ this.discount = wallet.discount
+ this.discount_description = wallet.discount_description
+ }
+
if (this.user_id === 'new') {
// do nothing (for now)
axios.get('/api/v4/packages')
@@ -167,6 +188,8 @@
this.user = response.data
this.user.first_name = response.data.settings.first_name
this.user.last_name = response.data.settings.last_name
+ this.discount = this.user.wallet.discount
+ this.discount_description = this.user.wallet.discount_description
$('#aliases').val(response.data.aliases.join("\n"))
listinput('#aliases')
@@ -310,12 +333,13 @@
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
// Update the label
input.prev().text(value + ' ' + sku.range.unit)
// Update the price
- record.find('.price').text(this.$root.price(sku.cost * (value - sku.units_free)) + '/month')
+ record.find('.price').text(this.price(cost, value - sku.units_free))
},
findSku(id) {
for (let i = 0; i < this.skus.length; i++) {
@@ -323,6 +347,16 @@
return this.skus[i];
}
}
+ },
+ price(cost, units = 1) {
+ let index = ''
+
+ if (this.discount) {
+ cost = Math.floor(cost * ((100 - this.discount) / 100))
+ index = '\u00B9'
+ }
+
+ return this.$root.price(cost * units) + '/month' + index
}
}
}
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
@@ -2,6 +2,7 @@
namespace Tests\Browser;
+use App\Discount;
use App\Entitlement;
use App\Sku;
use App\User;
@@ -40,6 +41,10 @@
->where('alias', 'john.test@kolab.org')->delete();
Entitlement::where('entitleable_id', $john->id)->whereIn('cost', [25, 100])->delete();
+
+ $wallet = $john->wallets()->first();
+ $wallet->discount()->dissociate();
+ $wallet->save();
}
/**
@@ -56,6 +61,10 @@
Entitlement::where('entitleable_id', $john->id)->whereIn('cost', [25, 100])->delete();
+ $wallet = $john->wallets()->first();
+ $wallet->discount()->dissociate();
+ $wallet->save();
+
parent::tearDown();
}
@@ -270,6 +279,7 @@
)
->click('tbody tr:nth-child(5) td.selection input');
})
+ ->assertMissing('@skus table + .hint')
->click('button[type=submit]');
})
->with(new Toast(Toast::TYPE_SUCCESS), function (Browser $browser) {
@@ -348,11 +358,14 @@
$browser->assertElementsCount('tbody tr', 2)
->assertSeeIn('tbody tr:nth-child(1)', 'Groupware Account')
->assertSeeIn('tbody tr:nth-child(2)', 'Lite Account')
+ ->assertSeeIn('tbody tr:nth-child(1) .price', '9,99 CHF/month')
+ ->assertSeeIn('tbody tr:nth-child(2) .price', '4,44 CHF/month')
->assertChecked('tbody tr:nth-child(1) input')
->click('tbody tr:nth-child(2) input')
->assertNotChecked('tbody tr:nth-child(1) input')
->assertChecked('tbody tr:nth-child(2) input');
})
+ ->assertMissing('@packages table + .hint')
->assertSeeIn('button[type=submit]', 'Submit');
// Test browser-side required fields and error handling
@@ -510,4 +523,63 @@
// TODO: Test what happens with the logged in user session after he's been deleted by another user
}
+
+ /**
+ * Test discounted sku/package prices in the UI
+ */
+ public function testDiscountedPrices(): void
+ {
+ // Add 10% discount
+ $discount = Discount::where('code', 'TEST')->first();
+ $john = User::where('email', 'john@kolab.org')->first();
+ $wallet = $john->wallet();
+ $wallet->discount()->associate($discount);
+ $wallet->save();
+
+ // SKUs on user edit page
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/logout')
+ ->on(new Home())
+ ->submitLogon('john@kolab.org', 'simple123', true)
+ ->visit(new UserList())
+ ->click('@table tr:nth-child(2) a')
+ ->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->assertElementsCount('tbody tr', 5)
+ // Mailbox SKU
+ ->assertSeeIn('tbody tr:nth-child(1) td.price', '3,99 CHF/month¹')
+ // Storage SKU
+ ->assertSeeIn('tr:nth-child(2) td.price', '0,00 CHF/month¹')
+ ->with($quota_input, function (Browser $browser) {
+ $browser->setQuotaValue(100);
+ })
+ ->assertSeeIn('tr:nth-child(2) td.price', '21,56 CHF/month¹')
+ // groupware SKU
+ ->assertSeeIn('tbody tr:nth-child(3) td.price', '4,99 CHF/month¹')
+ // 2FA SKU
+ ->assertSeeIn('tbody tr:nth-child(4) td.price', '0,00 CHF/month¹')
+ // ActiveSync SKU
+ ->assertSeeIn('tbody tr:nth-child(5) td.price', '0,90 CHF/month¹');
+ })
+ ->assertSeeIn('@skus table + .hint', '¹ applied discount: 10% - Test voucher');
+ });
+ });
+
+ // Packages on new user page
+ $this->browse(function (Browser $browser) {
+ $browser->visit(new UserList())
+ ->click('button.create-user')
+ ->on(new UserInfo())
+ ->with('@form', function (Browser $browser) {
+ $browser->whenAvailable('@packages', function (Browser $browser) {
+ $browser->assertElementsCount('tbody tr', 2)
+ ->assertSeeIn('tbody tr:nth-child(1) .price', '8,99 CHF/month¹') // Groupware
+ ->assertSeeIn('tbody tr:nth-child(2) .price', '3,99 CHF/month¹'); // Lite
+ })
+ ->assertSeeIn('@packages table + .hint', '¹ applied discount: 10% - Test voucher');
+ });
+ });
+ }
}
diff --git a/src/tests/Feature/BillingTest.php b/src/tests/Feature/BillingTest.php
--- a/src/tests/Feature/BillingTest.php
+++ b/src/tests/Feature/BillingTest.php
@@ -204,7 +204,7 @@
$this->assertEquals(2023, $this->wallet->expectedCharges());
}
- public function testWithDiscount(): void
+ public function testWithDiscountRate(): void
{
$package = \App\Package::create(
[
@@ -241,4 +241,19 @@
$this->assertEquals(500, $wallet->expectedCharges());
}
+
+ /**
+ * Test cost calculation with a wallet discount
+ */
+ public function testWithWalletDiscount(): void
+ {
+ $discount = \App\Discount::where('code', 'TEST')->first();
+
+ $wallet = $this->user->wallets()->first();
+ $wallet->discount()->associate($discount);
+
+ $this->backdateEntitlements($wallet->entitlements, Carbon::now()->subMonthsWithoutOverflow(1));
+
+ $this->assertEquals(898, $wallet->expectedCharges());
+ }
}
diff --git a/src/tests/Feature/Console/DiscountListTest.php b/src/tests/Feature/Console/DiscountListTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/DiscountListTest.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Tests\Feature\Console;
+
+use Tests\TestCase;
+
+class DiscountListTest extends TestCase
+{
+ public function testHandle(): void
+ {
+ $this->artisan('discount:list')
+ ->assertExitCode(0);
+
+ $this->markTestIncomplete();
+ }
+}
diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/UserDiscountTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/UserDiscountTest.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Tests\Feature\Console;
+
+use Tests\TestCase;
+
+class UserDiscountTest extends TestCase
+{
+ public function testHandle(): void
+ {
+ $this->markTestIncomplete();
+ }
+}
diff --git a/src/tests/Feature/Console/UserWalletsTest.php b/src/tests/Feature/Console/UserWalletsTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/UserWalletsTest.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Tests\Feature\Console;
+
+use Tests\TestCase;
+
+class UserWalletsTest extends TestCase
+{
+ public function testHandle(): void
+ {
+ $this->artisan('user:wallets john@kolab.org')
+ ->assertExitCode(0);
+
+ $this->markTestIncomplete();
+ }
+}
diff --git a/src/tests/Feature/Console/WalletDiscountTest.php b/src/tests/Feature/Console/WalletDiscountTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/WalletDiscountTest.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Tests\Feature\Console;
+
+use Tests\TestCase;
+
+class WalletDiscountTest extends TestCase
+{
+ public function testHandle(): void
+ {
+ $this->markTestIncomplete();
+ }
+}
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
@@ -2,11 +2,13 @@
namespace Tests\Feature\Controller;
+use App\Discount;
use App\Domain;
use App\Http\Controllers\API\UsersController;
use App\Package;
use App\Sku;
use App\User;
+use App\Wallet;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Str;
use Tests\TestCase;
@@ -26,6 +28,11 @@
$this->deleteTestUser('UserEntitlement2A@UserEntitlement.com');
$this->deleteTestUser('john2.doe2@kolab.org');
$this->deleteTestDomain('userscontroller.com');
+
+ $user = $this->getTestUser('john@kolab.org');
+ $wallet = $user->wallets()->first();
+ $wallet->discount()->dissociate();
+ $wallet->save();
}
/**
@@ -40,6 +47,11 @@
$this->deleteTestUser('john2.doe2@kolab.org');
$this->deleteTestDomain('userscontroller.com');
+ $user = $this->getTestUser('john@kolab.org');
+ $wallet = $user->wallets()->first();
+ $wallet->discount()->dissociate();
+ $wallet->save();
+
parent::tearDown();
}
@@ -382,6 +394,7 @@
$this->assertCount(0, $result['accounts']);
$this->assertCount(1, $result['wallets']);
$this->assertSame($wallet->id, $result['wallet']['id']);
+ $this->assertArrayNotHasKey('discount', $result['wallet']);
$ned = $this->getTestUser('ned@kolab.org');
$ned_wallet = $ned->wallets()->first();
@@ -396,6 +409,22 @@
$this->assertSame($wallet->id, $result['wallet']['id']);
$this->assertSame($wallet->id, $result['accounts'][0]['id']);
$this->assertSame($ned_wallet->id, $result['wallets'][0]['id']);
+
+ // Test discount in a response
+ $discount = Discount::where('code', 'TEST')->first();
+ $wallet->discount()->associate($discount);
+ $wallet->save();
+ $user->refresh();
+
+ $result = $this->invokeMethod(new UsersController(), 'userResponse', [$user]);
+
+ $this->assertEquals($user->id, $result['id']);
+ $this->assertSame($discount->id, $result['wallet']['discount_id']);
+ $this->assertSame($discount->discount, $result['wallet']['discount']);
+ $this->assertSame($discount->description, $result['wallet']['discount_description']);
+ $this->assertSame($discount->id, $result['wallets'][0]['discount_id']);
+ $this->assertSame($discount->discount, $result['wallets'][0]['discount']);
+ $this->assertSame($discount->description, $result['wallets'][0]['discount_description']);
}
/**
diff --git a/src/tests/Feature/DiscountTest.php b/src/tests/Feature/DiscountTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/DiscountTest.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\Discount;
+use Tests\TestCase;
+
+class DiscountTest extends TestCase
+{
+ /**
+ * Test setting discount value
+ */
+ public function testDiscountValueLessThanZero(): void
+ {
+ $this->expectException(\Exception::class);
+
+ $discount = new Discount();
+ $discount->discount = -1;
+ }
+
+ /**
+ * Test setting discount value
+ */
+ public function testDiscountValueMoreThanHundred(): void
+ {
+ $this->expectException(\Exception::class);
+
+ $discount = new Discount();
+ $discount->discount = 101;
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 5, 7:47 AM (3 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18832546
Default Alt Text
D1063.1775375264.diff (31 KB)

Event Timeline