diff --git a/src/app/Console/Commands/PlanPackagesCommand.php b/src/app/Console/Commands/PlanPackagesCommand.php index a3ec72cf..6125b5cf 100644 --- a/src/app/Console/Commands/PlanPackagesCommand.php +++ b/src/app/Console/Commands/PlanPackagesCommand.php @@ -1,65 +1,86 @@ info(sprintf("Plan: %s", $plan->title)); + $plan_costs = 0; + foreach ($plan->packages()->get() as $package) { + $qtyMin = $package->pivot->qty_min; + $qtyMax = $package->pivot->qty_max; + + $discountQty = $package->pivot->discount_qty; + $discountRate = (100 - $package->pivot->discount_rate) / 100; + $this->info( sprintf( - " Package: %s (min: %d, max: %d, discount %d%% after the first %d)", + " Package: %s (min: %d, max: %d, discount %d%% after the first %d, base cost: %d)", $package->title, $package->pivot->qty_min, $package->pivot->qty_max, $package->pivot->discount_rate, - $package->pivot->discount_qty + $package->pivot->discount_qty, + $package->cost() ) ); foreach ($package->skus()->get() as $sku) { $this->info(sprintf(" SKU: %s (%d)", $sku->title, $sku->pivot->qty)); } + + if ($qtyMin <= $discountQty) { + $plan_costs += $qtyMin * $package->cost(); + } else { + // base rate + $plan_costs += $discountQty * $package->cost(); + + // discounted rate + $plan_costs += ($qtyMin - $discountQty) * $package->cost() * $discountRate; + } } + + $this->info(sprintf(" Plan costs per month: %d", $plan_costs)); } } } diff --git a/src/app/Package.php b/src/app/Package.php index ce4d3bc5..f7fea74c 100644 --- a/src/app/Package.php +++ b/src/app/Package.php @@ -1,41 +1,52 @@ skus as $sku) { + $costs += ($sku->pivot->qty - $sku->units_free) * $sku->cost; + } + + return $costs; + } + public function skus() { return $this->belongsToMany('App\Sku', 'package_skus')->using('App\PackageSku')->withPivot(['qty']); } } diff --git a/src/app/Plan.php b/src/app/Plan.php index cc1e6648..746be3b6 100644 --- a/src/app/Plan.php +++ b/src/app/Plan.php @@ -1,54 +1,65 @@ 'datetime', 'promo_to' => 'datetime', 'discount_qty' => 'integer', 'discount_rate' => 'integer' ]; + public function cost() + { + $costs = 0; + + foreach ($this->packages as $package) { + $costs += $package->pivot->cost(); + } + + return $costs; + } + public function packages() { return $this->belongsToMany( 'App\Package', 'plan_packages' )->using('App\PlanPackage')->withPivot( [ 'qty_min', 'qty_max', 'discount_qty', 'discount_rate' ] ); } } diff --git a/src/app/PlanPackage.php b/src/app/PlanPackage.php index 68384fc6..4fb6867f 100644 --- a/src/app/PlanPackage.php +++ b/src/app/PlanPackage.php @@ -1,26 +1,39 @@ 'integer', 'qty_max' => 'integer', 'qty_min' => 'integer', 'discount_qty' => 'integer', 'discount_rate' => 'integer' ]; + + public function cost() + { + $costs = 0; + + if ($this->qty_min > 0) { + foreach ($this->package->skus() as $sku) { + $costs += $sku->cost; + } + } + + return $costs; + } } diff --git a/src/app/Sku.php b/src/app/Sku.php index b3e75786..3dc4bab7 100644 --- a/src/app/Sku.php +++ b/src/app/Sku.php @@ -1,42 +1,46 @@ 'integer' + ]; + protected $fillable = [ 'title', 'description', 'cost', 'units_free', 'period', 'handler_class', 'active' ]; /** * List the entitlements that consume this SKU. * * @return Entitlement[] */ public function entitlements() { return $this->hasMany('App\Entitlement'); } public function packages() { return $this->belongsToMany( 'App\Package', 'package_skus' )->using('App\PackageSku')->withPivot(['qty']); } } diff --git a/src/database/migrations/2019_12_10_095027_create_packages_table.php b/src/database/migrations/2019_12_10_095027_create_packages_table.php index f1f337c8..a08e34de 100644 --- a/src/database/migrations/2019_12_10_095027_create_packages_table.php +++ b/src/database/migrations/2019_12_10_095027_create_packages_table.php @@ -1,38 +1,38 @@ string('id', 36); $table->string('title', 36); $table->string('description', 128); - $table->integer('discount')->default(0)->nullable(); + $table->integer('discount_rate')->default(0)->nullable(); $table->primary('id'); } ); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('packages'); } } diff --git a/src/database/seeds/PackageSeeder.php b/src/database/seeds/PackageSeeder.php index 292d3f39..38129a2b 100644 --- a/src/database/seeds/PackageSeeder.php +++ b/src/database/seeds/PackageSeeder.php @@ -1,52 +1,76 @@ 'kolab', 'description' => 'A fully functional groupware account.', - 'discount' => 0 + 'discount_rate' => 0 ] ); $skus = [ Sku::firstOrCreate(['title' => 'mailbox']), Sku::firstOrCreate(['title' => 'storage']), Sku::firstOrCreate(['title' => 'groupware']) ]; $package->skus()->saveMany($skus); - $package->skus()->updateExistingPivot(Sku::firstOrCreate(['title' => 'storage']), array('qty' => 2), false); + // This package contains 2 units of the storage SKU, which just so happens to also + // be the number of SKU free units. + $package->skus()->updateExistingPivot( + Sku::firstOrCreate(['title' => 'storage']), + array('qty' => 2), + false + ); $package = Package::create( [ 'title' => 'lite', 'description' => 'Just mail and no more.', - 'discount' => 0 + 'discount_rate' => 0 ] ); $skus = [ Sku::firstOrCreate(['title' => 'mailbox']), Sku::firstOrCreate(['title' => 'storage']) ]; $package->skus()->saveMany($skus); - $package->skus()->updateExistingPivot(Sku::firstOrCreate(['title' => 'storage']), array('qty' => 2), false); + $package->skus()->updateExistingPivot( + Sku::firstOrCreate(['title' => 'storage']), + ['qty' => 2], + false + ); + + $package = Package::create( + [ + 'title' => 'domain-hosting', + 'description' => 'Use your own domain.', + 'discount_rate' => 0 + ] + ); + + $skus = [ + Sku::firstOrCreate(['title' => 'domain-hosting']) + ]; + + $package->skus()->saveMany($skus); } } diff --git a/src/database/seeds/PlanSeeder.php b/src/database/seeds/PlanSeeder.php index ae42a52a..1b63ebcb 100644 --- a/src/database/seeds/PlanSeeder.php +++ b/src/database/seeds/PlanSeeder.php @@ -1,107 +1,110 @@ 'family', 'description' => 'A group of accounts for 2 or more users.', 'discount_qty' => 0, 'discount_rate' => 0 ] ); $packages = [ Package::firstOrCreate(['title' => 'kolab']), + Package::firstOrCreate(['title' => 'domain-hosting']) ]; $plan->packages()->saveMany($packages); $plan->packages()->updateExistingPivot( Package::firstOrCreate(['title' => 'kolab']), [ 'qty_min' => 2, 'qty_max' => -1, 'discount_qty' => 2, 'discount_rate' => 50 ], false ); $plan = Plan::create( [ 'title' => 'small-business', 'description' => 'Accounts for small business owners.', 'discount_qty' => 0, 'discount_rate' => 10 ] ); $packages = [ Package::firstOrCreate(['title' => 'kolab']), + Package::firstOrCreate(['title' => 'domain-hosting']) ]; $plan->packages()->saveMany($packages); $plan->packages()->updateExistingPivot( Package::firstOrCreate(['title' => 'kolab']), [ 'qty_min' => 5, 'qty_max' => 25, 'discount_qty' => 5, 'discount_rate' => 30 ], false ); $plan = Plan::create( [ 'title' => 'large-business', 'description' => 'Accounts for large businesses.', 'discount_qty' => 0, 'discount_rate' => 10 ] ); $packages = [ Package::firstOrCreate(['title' => 'kolab']), Package::firstOrCreate(['title' => 'lite']), + Package::firstOrCreate(['title' => 'domain-hosting']) ]; $plan->packages()->saveMany($packages); $plan->packages()->updateExistingPivot( Package::firstOrCreate(['title' => 'kolab']), [ 'qty_min' => 20, 'qty_max' => -1, 'discount_qty' => 10, 'discount_rate' => 10 ], false ); $plan->packages()->updateExistingPivot( Package::firstOrCreate(['title' => 'lite']), [ 'qty_min' => 0, 'qty_max' => -1, 'discount_qty' => 10, 'discount_rate' => 10 ], false ); } } diff --git a/src/database/seeds/SkuSeeder.php b/src/database/seeds/SkuSeeder.php index e531671f..b4ebaa6e 100644 --- a/src/database/seeds/SkuSeeder.php +++ b/src/database/seeds/SkuSeeder.php @@ -1,117 +1,118 @@ 'mailbox', 'description' => 'Just a mailbox', - 'cost' => 152, - 'units_free' => 1, + 'cost' => 444, + 'units_free' => 0, 'period' => 'monthly', 'handler_class' => 'App\Handlers\Mailbox', 'active' => true, ] ); Sku::create( [ 'title' => 'domain', 'description' => 'Somewhere to place a mailbox', - 'cost' => 134, + 'cost' => 100, 'period' => 'monthly', 'handler_class' => 'App\Handlers\Domain', 'active' => true, ] ); Sku::create( [ 'title' => 'domain-registration', 'description' => 'Register a domain with us', - 'cost' => 135, + 'cost' => 101, 'period' => 'yearly', 'handler_class' => 'App\Handlers\DomainRegistration', 'active' => false, ] ); Sku::create( [ 'title' => 'domain-hosting', 'description' => 'Host a domain that is externally registered', - 'cost' => 136, + 'cost' => 100, + 'units_free' => 1, 'period' => 'monthly', 'handler_class' => 'App\Handlers\DomainHosting', 'active' => false, ] ); Sku::create( [ 'title' => 'domain-relay', 'description' => 'A domain you host at home, for which we relay email', - 'cost' => 137, + 'cost' => 103, 'period' => 'monthly', 'handler_class' => 'App\Handlers\DomainRelay', 'active' => false, ] ); Sku::create( [ 'title' => 'storage', 'description' => 'Some wiggle room', 'cost' => 25, 'units_free' => 2, 'period' => 'monthly', 'handler_class' => 'App\Handlers\Storage', 'active' => true, ] ); Sku::create( [ 'title' => 'groupware', 'description' => 'groupware functions', - 'cost' => 203, - 'units_free' => 1, + 'cost' => 555, + 'units_free' => 0, 'period' => 'monthly', 'handler_class' => 'App\Handlers\Groupware', 'active' => true, ] ); Sku::create( [ 'title' => 'resource', 'description' => 'Reservation taker', 'cost' => 101, 'period' => 'monthly', 'handler_class' => 'App\Handlers\Resource', 'active' => false, ] ); Sku::create( [ 'title' => 'shared_folder', 'description' => 'A shared folder', 'cost' => 89, 'period' => 'monthly', 'handler_class' => 'App\Handlers\SharedFolder', 'active' => false, ] ); } }