diff --git a/app/Entities/Projects/Project.php b/app/Entities/Projects/Project.php index 8e90be3..b3c249f 100755 --- a/app/Entities/Projects/Project.php +++ b/app/Entities/Projects/Project.php @@ -11,13 +11,14 @@ use App\Entities\Users\User; use Illuminate\Database\Eloquent\Model; use Laracasts\Presenter\PresentableTrait; -class Project extends Model { +class Project extends Model +{ use PresentableTrait; protected $presenter = ProjectPresenter::class; - protected $guarded = ['id','created_at','updated_at']; - // protected $dates = ['start_date','end_date']; + protected $guarded = ['id', 'created_at', 'updated_at']; + // protected $dates = ['start_date','end_date']; public function nameLink() { @@ -56,24 +57,24 @@ class Project extends Model { public function payments() { - return $this->hasMany(Payment::class)->orderBy('date','desc'); + return $this->hasMany(Payment::class)->orderBy('date', 'desc'); } public function customer() { - return $this->belongsTo(User::class,'customer_id'); + return $this->belongsTo(User::class, 'customer_id'); } public function cashInTotal() { - return $this->payments->sum(function($payment) { + return $this->payments->sum(function ($payment) { return $payment->in_out == 1 ? $payment->amount : 0; }); } public function cashOutTotal() { - return $this->payments->sum(function($payment) { + return $this->payments->sum(function ($payment) { return $payment->in_out == 0 ? $payment->amount : 0; }); } @@ -98,4 +99,18 @@ class Project extends Model { return $this->morphMany(File::class, 'fileable'); } + public function getCollectibeEarnings() + { + // Collectible earnings is total of (price * avg task progress of each feature) + $collectibeEarnings = 0; + $this->load('features.tasks'); + + foreach ($this->features as $feature) { + $progress = $feature->tasks->avg('progress'); + $collectibeEarnings += ($progress / 100) * $feature->price; + } + + return $collectibeEarnings; + } + } diff --git a/app/Entities/Users/User.php b/app/Entities/Users/User.php index 974d49b..183a6a3 100644 --- a/app/Entities/Users/User.php +++ b/app/Entities/Users/User.php @@ -71,21 +71,21 @@ class User extends Authenticatable return $this->roles->contains('name', $role); } - return !! $role->intersect($this->roles)->count(); + return !!$role->intersect($this->roles)->count(); } public function hasRoles(array $roleNameArray) { return $this->roles->pluck('name') - ->contains(function($role, $key) use ($roleNameArray) { + ->contains(function ($role, $key) use ($roleNameArray) { return in_array($role, $roleNameArray); }); } public function scopeHasRoles($query, array $roleNameArray) { - return $query->whereHas('roles', function($q) use ($roleNameArray) { - $q->whereIn('name',$roleNameArray); + return $query->whereHas('roles', function ($q) use ($roleNameArray) { + $q->whereIn('name', $roleNameArray); }); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 9ad2e76..1f4eafa 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,8 +2,6 @@ namespace App\Providers; -use App\Entities\Projects\Project; -use DB; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -15,7 +13,7 @@ class AppServiceProvider extends ServiceProvider */ public function boot() { - require_once app_path() . '/helpers.php'; + require_once app_path().'/helpers.php'; } /** diff --git a/app/helpers.php b/app/helpers.php index 7d40471..516678f 100755 --- a/app/helpers.php +++ b/app/helpers.php @@ -7,21 +7,22 @@ */ function formatNo($number) { - return number_format($number, 0,',','.'); + return number_format($number, 0, ',', '.'); } function formatRp($number) { - if ($number == 0) { return '-'; } - if ($number < 0) + if ($number == 0) {return '-';} + if ($number < 0) { return '- Rp. ' . formatNo(abs($number)); + } return 'Rp. ' . formatNo($number); } function formatDecimal($number) { - return number_format($number, 2,',','.'); + return number_format($number, 2, ',', '.'); } /** @@ -37,17 +38,17 @@ function delete_button($form_params = [], $button_label = 'Delete', $button_opti $form_params['class'] = isset($form_params['class']) ? $form_params['class'] : 'del-form'; $form_params['style'] = isset($form_params['style']) ? $form_params['style'] : 'display:inline'; - if (! isset($button_options['class'])) + if (!isset($button_options['class'])) { $button_options['class'] = 'pull-right'; + } - if (! isset($button_options['title'])) + if (!isset($button_options['title'])) { $button_options['title'] = 'Delete this record'; + } $htmlForm = Form::open($form_params); - if (!empty($hiddenFields)) - { - foreach ($hiddenFields as $k => $v) - { + if (!empty($hiddenFields)) { + foreach ($hiddenFields as $k => $v) { $htmlForm .= Form::hidden($k, $v); } } @@ -59,8 +60,9 @@ function delete_button($form_params = [], $button_label = 'Delete', $button_opti function formatDate($date) { - if (!$date || $date == '0000-00-00') + if (!$date || $date == '0000-00-00') { return null; + } $explodedDate = explode('-', $date); @@ -73,9 +75,11 @@ function formatDate($date) throw new App\Exceptions\InvalidDateException('Kesalahan format tanggal'); } -function dateId($date) { - if (is_null($date) || $date == '0000-00-00') +function dateId($date) +{ + if (is_null($date) || $date == '0000-00-00') { return '-'; + } $explodedDate = explode('-', $date); @@ -87,13 +91,16 @@ function dateId($date) { throw new App\Exceptions\InvalidDateException('Kesalahan format tanggal'); } -function monthNumber($number) { +function monthNumber($number) +{ return str_pad($number, 2, "0", STR_PAD_LEFT); } -function monthId($monthNumber) { - if (is_null($monthNumber)) +function monthId($monthNumber) +{ + if (is_null($monthNumber)) { return $monthNumber; + } $months = getMonths(); $monthNumber = monthNumber($monthNumber); @@ -135,13 +142,15 @@ function str_split_ucwords($string) function getDays() { - return $days = [1 => 'Senin','Selasa','Rabu','Kamis','Jumat','Sabtu']; + return $days = [1 => 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu']; } function getDay($dayIndex = null) { $days = getDays(); - if (!is_null($dayIndex) && in_array($dayIndex, range(1, 7))) return $days[$dayIndex]; + if (!is_null($dayIndex) && in_array($dayIndex, range(1, 7))) { + return $days[$dayIndex]; + } return '-'; } @@ -153,28 +162,17 @@ function sanitizeNumber($number) function formatSizeUnits($bytes) { - if ($bytes >= 1073741824) - { + if ($bytes >= 1073741824) { $bytes = number_format($bytes / 1073741824, 2) . ' GB'; - } - elseif ($bytes >= 1048576) - { + } elseif ($bytes >= 1048576) { $bytes = number_format($bytes / 1048576, 2) . ' MB'; - } - elseif ($bytes >= 1024) - { + } elseif ($bytes >= 1024) { $bytes = number_format($bytes / 1024, 2) . ' KB'; - } - elseif ($bytes > 1) - { + } elseif ($bytes > 1) { $bytes = $bytes . ' bytes'; - } - elseif ($bytes == 1) - { + } elseif ($bytes == 1) { $bytes = $bytes . ' byte'; - } - else - { + } else { $bytes = '0 bytes'; } @@ -188,18 +186,22 @@ function formatSizeUnits($bytes) * @param array $parameters URL Parameter * @param array $attributes The anchor tag atributes */ -function html_link_to_route($name, $title = null, $parameters = [], $attributes = []) { - if (array_key_exists('icon', $attributes)) +function html_link_to_route($name, $title = null, $parameters = [], $attributes = []) +{ + if (array_key_exists('icon', $attributes)) { $title = ' ' . $title; + } return app('html')->decode(link_to_route($name, $title, $parameters, $attributes)); } -function getProjectStatusesList($statusId = null) { - $statuses = [1 => 'Planned','On Progress','Done','Closed','Canceled','On Hold']; +function getProjectStatusesList($statusId = null) +{ + $statuses = [1 => 'Planned', 'On Progress', 'Done', 'Closed', 'Canceled', 'On Hold']; - if (is_null($statusId)) + if (is_null($statusId)) { return $statuses; + } if (array_key_exists($statusId, $statuses)) { return $statuses[$statusId]; @@ -208,7 +210,7 @@ function getProjectStatusesList($statusId = null) { return null; } -function dateDifference($date1 , $date2 , $differenceFormat = '%a' ) +function dateDifference($date1, $date2, $differenceFormat = '%a') { $datetime1 = date_create($date1); $datetime2 = date_create($date2); @@ -222,11 +224,13 @@ function paymentTypes($paymentTypeId = null) { $paymentTypes = [1 => 'Project', 'Add Feature', 'Maintenance']; - if (is_null($paymentTypeId)) + if (is_null($paymentTypeId)) { return $paymentTypes; + } - if (array_key_exists($paymentTypeId, $paymentTypes)) + if (array_key_exists($paymentTypeId, $paymentTypes)) { return $paymentTypes[$paymentTypeId]; + } return null; -} \ No newline at end of file +} diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php index 03ed0f3..9ccccfb 100644 --- a/database/factories/ModelFactory.php +++ b/database/factories/ModelFactory.php @@ -7,16 +7,15 @@ use App\Entities\Projects\Project; use App\Entities\Projects\Task; use App\Entities\Subscriptions\Subscription; use App\Entities\Users\Event; -use App\Entities\Users\Role; use App\Entities\Users\User; $factory->define(User::class, function (Faker\Generator $faker) { return [ - 'name' => $faker->name, - 'email' => $faker->unique()->email, - 'password' => 'member', + 'name' => $faker->name, + 'email' => $faker->unique()->email, + 'password' => 'member', 'remember_token' => str_random(10), - 'api_token' => str_random(40), + 'api_token' => str_random(40), ]; }); @@ -24,21 +23,21 @@ $factory->define(Project::class, function (Faker\Generator $faker) { $proposalDate = $faker->dateTimeBetween('-1 year', '-1 month')->format('Y-m-d'); $startDate = Carbon::parse($proposalDate)->addDays(10); - $endDate = $startDate->addDays(rand(1,13) * 7); + $endDate = $startDate->addDays(rand(1, 13) * 7); return [ - 'name' => $faker->sentence(3), - 'description' => $faker->paragraph, - 'proposal_date' => $proposalDate, - 'start_date' => $startDate->format('Y-m-d'), - 'end_date' => $endDate->format('Y-m-d'), - 'project_value' => $projectValue = rand(1,10) * 500000, + 'name' => $faker->sentence(3), + 'description' => $faker->paragraph, + 'proposal_date' => $proposalDate, + 'start_date' => $startDate->format('Y-m-d'), + 'end_date' => $endDate->format('Y-m-d'), + 'project_value' => $projectValue = rand(1, 10) * 500000, 'proposal_value' => $projectValue, - 'status_id' => rand(1,6), - 'owner_id' => function () { + 'status_id' => rand(1, 6), + 'owner_id' => function () { return factory(User::class)->create()->id; }, - 'customer_id' => function () { + 'customer_id' => function () { return factory(User::class)->create()->id; }, ]; @@ -47,15 +46,15 @@ $factory->define(Project::class, function (Faker\Generator $faker) { $factory->define(Payment::class, function (Faker\Generator $faker) { return [ - 'project_id' => function () { + 'project_id' => function () { return factory(Project::class)->create()->id; }, - 'amount' => rand(1,5) * 500000, - 'in_out' => rand(0,1), - 'type_id' => rand(1,3), - 'date' => $faker->dateTimeBetween('-1 year', '-1 month')->format('Y-m-d'), + 'amount' => rand(1, 5) * 500000, + 'in_out' => rand(0, 1), + 'type_id' => rand(1, 3), + 'date' => $faker->dateTimeBetween('-1 year', '-1 month')->format('Y-m-d'), 'description' => $faker->paragraph, - 'owner_id' => function () { + 'owner_id' => function () { return factory(User::class)->create()->id; }, 'customer_id' => function () { @@ -69,22 +68,22 @@ $factory->define(Subscription::class, function (Faker\Generator $faker) { $startDate = Carbon::parse($faker->dateTimeBetween('-1 year', '-1 month')->format('Y-m-d')); return [ - 'project_id' => function () { + 'project_id' => function () { return factory(Project::class)->create()->id; }, - 'status_id' => 1, - 'domain_name' => 'www.' . str_random(10) . '.com', - 'domain_price' => 125000, - 'epp_code' => str_random(10), - 'hosting_capacity' => rand(1,3) . ' GB', - 'hosting_price' => rand(1,5) * 100000, - 'start_date' => $startDate->format('Y-m-d'), - 'due_date' => $startDate->addYears(1)->format('Y-m-d'), - 'remark' => $faker->paragraph, - 'customer_id' => function () { + 'status_id' => 1, + 'domain_name' => 'www.' . str_random(10) . '.com', + 'domain_price' => 125000, + 'epp_code' => str_random(10), + 'hosting_capacity' => rand(1, 3) . ' GB', + 'hosting_price' => rand(1, 5) * 100000, + 'start_date' => $startDate->format('Y-m-d'), + 'due_date' => $startDate->addYears(1)->format('Y-m-d'), + 'remark' => $faker->paragraph, + 'customer_id' => function () { return factory(User::class)->create()->id; }, - 'vendor_id' => function () { + 'vendor_id' => function () { return factory(User::class)->create()->id; }, ]; @@ -93,62 +92,62 @@ $factory->define(Subscription::class, function (Faker\Generator $faker) { $factory->define(Feature::class, function (Faker\Generator $faker) { return [ - 'project_id' => function () { + 'project_id' => function () { return factory(Project::class)->create()->id; }, - 'name' => $faker->sentence(3), - 'price' => rand(1,10) * 100000, + 'name' => $faker->sentence(3), + 'price' => rand(1, 10) * 100000, 'description' => $faker->paragraph, - 'worker_id' => function () { + 'worker_id' => function () { return factory(User::class)->create()->id; }, - 'type_id' => rand(1,2), - 'position' => rand(1,10), + 'type_id' => 1, // Main feature + 'position' => rand(1, 10), ]; }); $factory->define(Task::class, function (Faker\Generator $faker) { return [ - 'feature_id' => function () { + 'feature_id' => function () { return factory(Feature::class)->create()->id; }, - 'name' => $faker->sentence(3), + 'name' => $faker->sentence(3), 'description' => $faker->paragraph, - 'progress' => rand(40,100), - 'route_name' => implode('.', $faker->words(3)), - 'position' => rand(1,10), + 'progress' => rand(40, 100), + 'route_name' => implode('.', $faker->words(3)), + 'position' => rand(1, 10), ]; }); $factory->define(Event::class, function (Faker\Generator $faker) { return [ - 'user_id' => function () { + 'user_id' => function () { return factory(User::class)->create()->id; }, 'project_id' => function () { return factory(Project::class)->create()->id; }, - 'title' => $faker->words(rand(2,4), true), - 'body' => $faker->sentence, - 'start' => $faker->dateTimeBetween('-2 months', '-2 months')->format('Y-m-d H:i:s'), - 'end' => $faker->dateTimeBetween('-2 months', '-2 months')->format('Y-m-d H:i:s'), - 'is_allday' => rand(0,1), + 'title' => $faker->words(rand(2, 4), true), + 'body' => $faker->sentence, + 'start' => $faker->dateTimeBetween('-2 months', '-2 months')->format('Y-m-d H:i:s'), + 'end' => $faker->dateTimeBetween('-2 months', '-2 months')->format('Y-m-d H:i:s'), + 'is_allday' => rand(0, 1), ]; }); $factory->define(Invoice::class, function (Faker\Generator $faker) { $invoice = new Invoice; return [ - 'project_id' => function () { + 'project_id' => function () { return factory(Project::class)->create()->id; }, - 'number' => $invoice->generateNewNumber(), - 'items' => [], - 'amount' => 100000, - 'notes' => $faker->paragraph, - 'status_id' => 1, - 'user_id' => 1, + 'number' => $invoice->generateNewNumber(), + 'items' => [], + 'amount' => 100000, + 'notes' => $faker->paragraph, + 'status_id' => 1, + 'user_id' => 1, ]; -}); \ No newline at end of file +}); diff --git a/resources/views/projects/partials/project-show.blade.php b/resources/views/projects/partials/project-show.blade.php index cbead0a..494126c 100644 --- a/resources/views/projects/partials/project-show.blade.php +++ b/resources/views/projects/partials/project-show.blade.php @@ -23,4 +23,4 @@ - \ No newline at end of file + diff --git a/resources/views/projects/partials/project-stats.blade.php b/resources/views/projects/partials/project-stats.blade.php index c11f363..2de6f00 100644 --- a/resources/views/projects/partials/project-stats.blade.php +++ b/resources/views/projects/partials/project-stats.blade.php @@ -15,7 +15,7 @@
diff --git a/resources/views/projects/show.blade.php b/resources/views/projects/show.blade.php index 0afe825..362e447 100755 --- a/resources/views/projects/show.blade.php +++ b/resources/views/projects/show.blade.php @@ -27,4 +27,4 @@ {!! Form::close() !!} -@endsection \ No newline at end of file +@endsection diff --git a/tests/Unit/Models/InvoiceTest.php b/tests/Unit/Models/InvoiceTest.php index fdaba7b..c865376 100644 --- a/tests/Unit/Models/InvoiceTest.php +++ b/tests/Unit/Models/InvoiceTest.php @@ -19,9 +19,9 @@ class InvoiceTest extends TestCase public function it_generates_its_own_number() { $invoice1 = factory(Invoice::class)->create(); - $this->assertEquals(date('ym').'001', $invoice1->number); + $this->assertEquals(date('ym') . '001', $invoice1->number); $invoice2 = factory(Invoice::class)->create(); - $this->assertEquals(date('ym').'002', $invoice2->number); + $this->assertEquals(date('ym') . '002', $invoice2->number); } } diff --git a/tests/Unit/Models/ProjectTest.php b/tests/Unit/Models/ProjectTest.php index ddb62e9..768f2ba 100644 --- a/tests/Unit/Models/ProjectTest.php +++ b/tests/Unit/Models/ProjectTest.php @@ -138,4 +138,33 @@ class ProjectTest extends TestCase $project = factory(Project::class)->make(); $this->assertEquals(link_to_route('projects.show', $project->name, [$project->id]), $project->nameLink()); } + + /** @test */ + public function a_project_has_collectible_earnings_method() + { + // Collectible earnings is total of (price * avg task progress of each feature) + $project = factory(Project::class)->create(); + + $collectibeEarnings = 0; + + $feature = factory(Feature::class)->create(['project_id' => $project->id, 'type_id' => 1, 'price' => 2000]); + factory(Task::class)->create(['feature_id' => $feature->id, 'progress' => 20]); + $collectibeEarnings += (2000 * (20 / 100)); // feature price * avg task progress + + $feature = factory(Feature::class)->create(['project_id' => $project->id, 'type_id' => 1, 'price' => 3000]); + factory(Task::class)->create(['feature_id' => $feature->id, 'progress' => 30]); + $collectibeEarnings += (3000 * (30 / 100)); + + $feature = factory(Feature::class)->create(['project_id' => $project->id, 'type_id' => 1, 'price' => 1500]); + factory(Task::class)->create(['feature_id' => $feature->id, 'progress' => 100]); + $collectibeEarnings += (1500 * (100 / 100)); + + $feature = factory(Feature::class)->create(['project_id' => $project->id, 'type_id' => 1, 'price' => 1500]); + factory(Task::class)->create(['feature_id' => $feature->id, 'progress' => 100]); + $collectibeEarnings += (1500 * (100 / 100)); + + // $collectibeEarnings = 400 + 900 + 1500 + 1500; + + $this->assertEquals($collectibeEarnings, $project->getCollectibeEarnings()); + } } diff --git a/tests/Unit/Models/UserTest.php b/tests/Unit/Models/UserTest.php index f821ef4..dde0b96 100644 --- a/tests/Unit/Models/UserTest.php +++ b/tests/Unit/Models/UserTest.php @@ -13,7 +13,7 @@ class UserTest extends TestCase $user = factory(User::class)->create(); $this->assertEquals(link_to_route('users.show', $user->name, [$user->id], [ - 'target' => '_blank' + 'target' => '_blank', ]), $user->nameLink()); }