Page MenuHomePhorge

D4253.1774880330.diff
No OneTemporary

Authored By
Unknown
Size
15 KB
Referenced Files
None
Subscribers
None

D4253.1774880330.diff

diff --git a/src/app/Console/Commands/Wallet/ChargeCommand.php b/src/app/Console/Commands/Wallet/ChargeCommand.php
--- a/src/app/Console/Commands/Wallet/ChargeCommand.php
+++ b/src/app/Console/Commands/Wallet/ChargeCommand.php
@@ -65,9 +65,7 @@
$charge = $wallet->chargeEntitlements();
if ($charge > 0) {
- $this->info(
- "Charged wallet {$wallet->id} for user {$wallet->owner->email} with {$charge}"
- );
+ $this->info("Charged wallet {$wallet->id} for user {$wallet->owner->email} with {$charge}");
// Top-up the wallet if auto-payment enabled for the wallet
\App\Jobs\WalletCharge::dispatch($wallet);
diff --git a/src/app/Http/Controllers/API/SignupController.php b/src/app/Http/Controllers/API/SignupController.php
--- a/src/app/Http/Controllers/API/SignupController.php
+++ b/src/app/Http/Controllers/API/SignupController.php
@@ -431,7 +431,7 @@
$result = [];
$min = \App\Payment::MIN_AMOUNT;
- $planCost = $cost = $plan->cost() * $plan->months;
+ $planCost = $cost = $plan->cost();
$disc = 0;
if ($discount) {
diff --git a/src/app/Package.php b/src/app/Package.php
--- a/src/app/Package.php
+++ b/src/app/Package.php
@@ -69,6 +69,7 @@
$costs = 0;
foreach ($this->skus as $sku) {
+ // Note: This cost already takes package's discount_rate
$costs += $sku->pivot->cost();
}
@@ -98,6 +99,6 @@
{
return $this->belongsToMany(Sku::class, 'package_skus')
->using(PackageSku::class)
- ->withPivot(['qty']);
+ ->withPivot(['qty', 'cost']);
}
}
diff --git a/src/app/PackageSku.php b/src/app/PackageSku.php
--- a/src/app/PackageSku.php
+++ b/src/app/PackageSku.php
@@ -32,17 +32,29 @@
'qty' => 'integer'
];
+ /** @var array<int, string> The attributes that can be not set */
+ protected $nullable = [
+ 'cost',
+ ];
+
+ /** @var string Database table name */
+ protected $table = 'package_skus';
+
+ /** @var bool Indicates if the model should be timestamped. */
+ public $timestamps = false;
+
+
/**
* Under this package, how much does this SKU cost?
*
* @return int The costs of this SKU under this package in cents.
*/
- public function cost()
+ public function cost(): int
{
$units = $this->qty - $this->sku->units_free;
if ($units < 0) {
- $units = 0;
+ return 0;
}
// one way is to set a very nice looking price in the package_sku->cost
@@ -54,10 +66,14 @@
// 1189.15 that needs to be rounded and ends up 1189
//
// additional discounts could come from discount vouchers
- if ($this->cost > 0) {
+
+ // Side-note: Package's discount_rate is on a higher level, so conceptually
+ // I wouldn't be surprised if one would expect it to apply to package_sku.cost.
+
+ if ($this->cost !== null) {
$ppu = $this->cost;
} else {
- $ppu = $this->sku->cost * ((100 - $this->package->discount_rate) / 100);
+ $ppu = round($this->sku->cost * ((100 - $this->package->discount_rate) / 100));
}
return $units * $ppu;
diff --git a/src/app/Plan.php b/src/app/Plan.php
--- a/src/app/Plan.php
+++ b/src/app/Plan.php
@@ -77,19 +77,23 @@
];
/**
- * The list price for this package at the minimum configuration.
+ * The list price for this plan at the minimum configuration.
*
* @return int The costs in cents.
*/
- public function cost()
+ public function cost(): int
{
$costs = 0;
+ // TODO: What about plan's discount_qty/discount_rate?
+
foreach ($this->packages as $package) {
$costs += $package->pivot->cost();
}
- return $costs;
+ // TODO: What about plan's free_months?
+
+ return $costs * $this->months;
}
/**
diff --git a/src/app/PlanPackage.php b/src/app/PlanPackage.php
--- a/src/app/PlanPackage.php
+++ b/src/app/PlanPackage.php
@@ -40,14 +40,16 @@
];
/**
- * Calculate the costs for this plan.
+ * Calculate the costs for this package.
*
- * @return integer
+ * @return int The costs in cents
*/
public function cost()
{
$costs = 0;
+ // TODO: consider discount_qty/discount_rate here?
+
if ($this->qty_min > 0) {
$costs += $this->package->cost() * $this->qty_min;
} elseif ($this->qty > 0) {
diff --git a/src/app/Wallet.php b/src/app/Wallet.php
--- a/src/app/Wallet.php
+++ b/src/app/Wallet.php
@@ -447,7 +447,7 @@
$min = Payment::MIN_AMOUNT;
if ($plan = $this->plan()) {
- $planCost = (int) ($plan->cost() * $plan->months * $this->getDiscountRate());
+ $planCost = (int) ($plan->cost() * $this->getDiscountRate());
if ($planCost > $min) {
$min = $planCost;
diff --git a/src/database/migrations/2023_04_11_100000_plan_packages_cost_default.php b/src/database/migrations/2023_04_11_100000_plan_packages_cost_default.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2023_04_11_100000_plan_packages_cost_default.php
@@ -0,0 +1,38 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::table(
+ 'package_skus',
+ function (Blueprint $table) {
+ $table->integer('cost')->default(null)->nullable()->change();
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table(
+ 'package_skus',
+ function (Blueprint $table) {
+ $table->integer('cost')->default(0)->nullable()->change();
+ }
+ );
+ }
+};
diff --git a/src/tests/Feature/PackageTest.php b/src/tests/Feature/PackageTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/PackageTest.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\Entitlement;
+use App\Package;
+use App\PackageSku;
+use App\Sku;
+use Tests\TestCase;
+
+class PackageTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ Package::where('title', 'test-package')->delete();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ Package::where('title', 'test-package')->delete();
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test for a package's cost.
+ */
+ public function testCost(): void
+ {
+ $skuGroupware = Sku::withEnvTenantContext()->where('title', 'groupware')->first(); // cost: 490
+ $skuMailbox = Sku::withEnvTenantContext()->where('title', 'mailbox')->first(); // cost: 500
+ $skuStorage = Sku::withEnvTenantContext()->where('title', 'storage')->first(); // cost: 25
+
+ $package = Package::create([
+ 'title' => 'test-package',
+ 'name' => 'Test Account',
+ 'description' => 'Test account.',
+ 'discount_rate' => 0,
+ ]);
+
+ // WARNING: saveMany() sets package_skus.cost = skus.cost, the next line will reset it to NULL
+ $package->skus()->saveMany([
+ $skuMailbox,
+ $skuGroupware,
+ $skuStorage
+ ]);
+
+ PackageSku::where('package_id', $package->id)->update(['cost' => null]);
+
+ // Test a package w/o any extra parameters
+ $this->assertSame(490 + 500, $package->cost());
+
+ // Test a package with pivot's qty
+ $package->skus()->updateExistingPivot(
+ $skuStorage,
+ ['qty' => 6],
+ false
+ );
+ $package->refresh();
+
+ $this->assertSame(490 + 500 + 25, $package->cost());
+
+ // Test a package with pivot's cost
+ $package->skus()->updateExistingPivot(
+ $skuStorage,
+ ['cost' => 100],
+ false
+ );
+ $package->refresh();
+
+ $this->assertSame(490 + 500 + 100, $package->cost());
+
+ // Test a package with discount_rate
+ $package->discount_rate = 30;
+ $package->save();
+ $package->skus()->updateExistingPivot(
+ $skuMailbox,
+ ['qty' => 2],
+ false
+ );
+ $package->refresh();
+
+ $this->assertSame((int) (round(490 * 0.7) + 2 * round(500 * 0.7) + 100), $package->cost());
+ }
+}
diff --git a/src/tests/Feature/PlanTest.php b/src/tests/Feature/PlanTest.php
--- a/src/tests/Feature/PlanTest.php
+++ b/src/tests/Feature/PlanTest.php
@@ -86,25 +86,23 @@
*/
public function testCost(): void
{
- $plan = Plan::where('title', 'individual')->first();
-
- $package_costs = 0;
+ $orig_plan = Plan::withEnvTenantContext()->where('title', 'individual')->first();
+ $plan = Plan::create([
+ 'title' => 'test-plan',
+ 'description' => 'Test',
+ 'name' => 'Test',
+ ]);
- foreach ($plan->packages as $package) {
- $package_costs += $package->cost();
- }
+ $plan->packages()->saveMany($orig_plan->packages);
+ $plan->refresh();
- $this->assertTrue(
- $package_costs == 990,
- "The total costs of all packages for this plan is not 9.90"
- );
+ $this->assertSame(990, $plan->cost());
- $this->assertTrue(
- $plan->cost() == 990,
- "The total costs for this plan is not 9.90"
- );
+ // Test plan months != 1
+ $plan->months = 12;
+ $plan->save();
- $this->assertTrue($plan->cost() == $package_costs);
+ $this->assertSame(990 * 12, $plan->cost());
}
/**
diff --git a/src/tests/Feature/UserTest.php b/src/tests/Feature/UserTest.php
--- a/src/tests/Feature/UserTest.php
+++ b/src/tests/Feature/UserTest.php
@@ -4,6 +4,9 @@
use App\Domain;
use App\Group;
+use App\Package;
+use App\PackageSku;
+use App\Sku;
use App\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\Queue;
@@ -27,6 +30,7 @@
$this->deleteTestSharedFolder('test-folder@UserAccount.com');
$this->deleteTestDomain('UserAccount.com');
$this->deleteTestDomain('UserAccountAdd.com');
+ Package::where('title', 'test-package')->delete();
}
/**
@@ -35,6 +39,7 @@
public function tearDown(): void
{
\App\TenantSetting::truncate();
+ Package::where('title', 'test-package')->delete();
$this->deleteTestUser('user-test@' . \config('app.domain'));
$this->deleteTestUser('UserAccountA@UserAccount.com');
$this->deleteTestUser('UserAccountB@UserAccount.com');
@@ -55,22 +60,51 @@
{
$user = $this->getTestUser('user-test@' . \config('app.domain'));
$wallet = $user->wallets()->first();
+ $skuGroupware = Sku::withEnvTenantContext()->where('title', 'groupware')->first(); // cost: 490
+ $skuMailbox = Sku::withEnvTenantContext()->where('title', 'mailbox')->first(); // cost: 500
+ $skuStorage = Sku::withEnvTenantContext()->where('title', 'storage')->first(); // cost: 25
+ $package = Package::create([
+ 'title' => 'test-package',
+ 'name' => 'Test Account',
+ 'description' => 'Test account.',
+ 'discount_rate' => 0,
+ ]);
- $package = \App\Package::withEnvTenantContext()->where('title', 'kolab')->first();
+ // WARNING: saveMany() sets package_skus.cost = skus.cost
+ $package->skus()->saveMany([
+ $skuMailbox,
+ $skuGroupware,
+ $skuStorage
+ ]);
+
+ $package->skus()->updateExistingPivot($skuStorage, ['qty' => 2, 'cost' => null], false);
+ $package->skus()->updateExistingPivot($skuMailbox, ['cost' => null], false);
+ $package->skus()->updateExistingPivot($skuGroupware, ['cost' => 100], false);
$user->assignPackage($package);
- $sku = \App\Sku::withEnvTenantContext()->where('title', 'mailbox')->first();
+ $this->assertCount(4, $user->entitlements()->get()); // mailbox + groupware + 2 x storage
- $entitlement = \App\Entitlement::where('wallet_id', $wallet->id)
- ->where('sku_id', $sku->id)->first();
+ $entitlement = $wallet->entitlements()->where('sku_id', $skuMailbox->id)->first();
+ $this->assertSame($skuMailbox->id, $entitlement->sku->id);
+ $this->assertSame($wallet->id, $entitlement->wallet->id);
+ $this->assertEquals($user->id, $entitlement->entitleable_id);
+ $this->assertTrue($entitlement->entitleable instanceof \App\User);
+ $this->assertSame($skuMailbox->cost, $entitlement->cost);
- $this->assertNotNull($entitlement);
- $this->assertSame($sku->id, $entitlement->sku->id);
+ $entitlement = $wallet->entitlements()->where('sku_id', $skuGroupware->id)->first();
+ $this->assertSame($skuGroupware->id, $entitlement->sku->id);
$this->assertSame($wallet->id, $entitlement->wallet->id);
- $this->assertEquals($user->id, $entitlement->entitleable->id);
+ $this->assertEquals($user->id, $entitlement->entitleable_id);
$this->assertTrue($entitlement->entitleable instanceof \App\User);
- $this->assertCount(7, $user->entitlements()->get());
+ $this->assertSame(100, $entitlement->cost);
+
+ $entitlement = $wallet->entitlements()->where('sku_id', $skuStorage->id)->first();
+ $this->assertSame($skuStorage->id, $entitlement->sku->id);
+ $this->assertSame($wallet->id, $entitlement->wallet->id);
+ $this->assertEquals($user->id, $entitlement->entitleable_id);
+ $this->assertTrue($entitlement->entitleable instanceof \App\User);
+ $this->assertSame(0, $entitlement->cost);
}
/**
@@ -86,7 +120,36 @@
*/
public function testAssignSku(): void
{
- $this->markTestIncomplete();
+ $user = $this->getTestUser('user-test@' . \config('app.domain'));
+ $wallet = $user->wallets()->first();
+ $skuStorage = Sku::withEnvTenantContext()->where('title', 'storage')->first();
+ $skuMailbox = Sku::withEnvTenantContext()->where('title', 'mailbox')->first();
+
+ $user->assignSku($skuMailbox);
+
+ $this->assertCount(1, $user->entitlements()->get());
+ $entitlement = $wallet->entitlements()->where('sku_id', $skuMailbox->id)->first();
+ $this->assertSame($skuMailbox->id, $entitlement->sku->id);
+ $this->assertSame($wallet->id, $entitlement->wallet->id);
+ $this->assertEquals($user->id, $entitlement->entitleable_id);
+ $this->assertTrue($entitlement->entitleable instanceof \App\User);
+ $this->assertSame($skuMailbox->cost, $entitlement->cost);
+
+ // Test units_free handling
+ for ($x = 0; $x < 5; $x++) {
+ $user->assignSku($skuStorage);
+ }
+
+ $entitlements = $user->entitlements()->where('sku_id', $skuStorage->id)
+ ->where('cost', 0)
+ ->get();
+ $this->assertCount(5, $entitlements);
+
+ $user->assignSku($skuStorage);
+ $entitlements = $user->entitlements()->where('sku_id', $skuStorage->id)
+ ->where('cost', $skuStorage->cost)
+ ->get();
+ $this->assertCount(1, $entitlements);
}
/**

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 30, 2:18 PM (1 w, 18 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18813249
Default Alt Text
D4253.1774880330.diff (15 KB)

Event Timeline