Browse Source

Merge branch 'master' of https://github.com/nafiesl/free-pmo into ui-ux-improvements

pull/43/head
Damilola Olowookere 6 years ago
parent
commit
9b5d7b14be
  1. 2
      .travis.yml
  2. 4
      README.id.md
  3. 4
      README.md
  4. 16
      app/Entities/Projects/JobsRepository.php
  5. 3
      app/Http/Controllers/Auth/ResetPasswordController.php
  6. 3
      app/Http/Controllers/InstallationController.php
  7. 2
      app/Http/Controllers/References/SiteOptionsController.php
  8. 3
      app/Http/Controllers/Users/UsersController.php
  9. 3
      app/Services/InvoiceDraft/InvoiceDraftCollection.php
  10. 2
      composer.json
  11. 1871
      composer.lock
  12. 5
      database/factories/BankAccountFactory.php
  13. 5
      database/factories/ModelFactory.php
  14. 7
      database/factories/SubscriptionFactory.php
  15. 2
      resources/views/invoice-drafts/partials/form-draft-detail.blade.php
  16. 3
      resources/views/invoices/partials/detail.blade.php
  17. 2
      resources/views/payments/create.blade.php
  18. 22
      resources/views/projects/jobs/add-from-other-project.blade.php
  19. 4
      resources/views/projects/payments.blade.php
  20. 32
      tests/Feature/ManageJobsTest.php
  21. 36
      tests/Feature/Payments/ManagePaymentsTest.php

2
.travis.yml

@ -1,7 +1,7 @@
language: php language: php
php: php:
- 7.1
- 7.2
before_script: before_script:
- travis_retry composer self-update - travis_retry composer self-update

4
README.id.md

@ -41,7 +41,7 @@ Tujuan utama Free PMO adalah membantu pengelolaan data project dengan mudah dan
Aplikasi ini dapat dipasang dalam server lokal (PC/Laptop) dan server online, dengan spesifikasi berikut : Aplikasi ini dapat dipasang dalam server lokal (PC/Laptop) dan server online, dengan spesifikasi berikut :
#### Spesifikasi minimum server #### Spesifikasi minimum server
1. PHP 7.1.3 (dan memenuhi [server requirement Laravel 5.8](https://laravel.com/docs/5.8#server-requirements)),
1. PHP >= 7.2.0 (dan memenuhi [server requirement Laravel 6.x](https://laravel.com/docs/6.x#server-requirements)),
2. MySQL atau MariaDB, 2. MySQL atau MariaDB,
3. SQlite (untuk automated testing). 3. SQlite (untuk automated testing).
@ -72,7 +72,7 @@ Project ini dikembangkan oleh [Nafies Luthfi](https://github.com/nafiesl) dan pa
Free PMO dibangun menggunakan [metode TDD](https://blog.nafies.id/laravel/testing-laravel-tentang-automated-testing) dengan bahan dan dukungan dari paket-paket berikut ini : Free PMO dibangun menggunakan [metode TDD](https://blog.nafies.id/laravel/testing-laravel-tentang-automated-testing) dengan bahan dan dukungan dari paket-paket berikut ini :
##### Dependencies ##### Dependencies
* [Framework Laravel](https://laravel.com/docs/5.8) (versi 5.2 s/d 5.8).
* [Framework Laravel](https://laravel.com/docs/6.x) (versi 5.2 s/d 6.x).
* [luthfi/formfield](https://github.com/nafiesl/FormField), Wrapper Form dari [laravelcollective/html](https://github.com/laravelcollective/html) dengan Bootstrap 3. * [luthfi/formfield](https://github.com/nafiesl/FormField), Wrapper Form dari [laravelcollective/html](https://github.com/laravelcollective/html) dengan Bootstrap 3.
* [riskihajar/terbilang](https://github.com/riskihajar/terbilang), membuat angka terbilang (pada fitur cetak kuitansi) dan romawi. * [riskihajar/terbilang](https://github.com/riskihajar/terbilang), membuat angka terbilang (pada fitur cetak kuitansi) dan romawi.

4
README.md

@ -43,7 +43,7 @@ Free PMO was built for easy and professional project management.
This application can be installed on local server and online server with these specifications : This application can be installed on local server and online server with these specifications :
#### Server Requirements #### Server Requirements
1. PHP 7.1.3 (and meet [Laravel 5.8 server requirements](https://laravel.com/docs/5.8#server-requirements)),
1. PHP >= 7.2.0 (and meet [Laravel 6.x server requirements](https://laravel.com/docs/6.x#server-requirements)),
2. MySQL or MariaDB database, 2. MySQL or MariaDB database,
3. SQlite (for automated testing). 3. SQlite (for automated testing).
@ -74,7 +74,7 @@ This project maintained by [Nafies Luthfi](https://github.com/nafiesl) and devel
Free PMO built with [TDD metode](https://blog.nafies.id/laravel/testing-laravel-tentang-automated-testing) with these ingredients support : Free PMO built with [TDD metode](https://blog.nafies.id/laravel/testing-laravel-tentang-automated-testing) with these ingredients support :
##### Dependencies ##### Dependencies
* [Framework Laravel](https://laravel.com/docs/5.8) (version 5.2 to 5.8).
* [Framework Laravel](https://laravel.com/docs/6.x) (version 5.2 to 6.x).
* [luthfi/formfield](https://github.com/nafiesl/FormField), Bootstrap 3 Form Wrapper for [laravelcollective/html](https://github.com/laravelcollective/html). * [luthfi/formfield](https://github.com/nafiesl/FormField), Bootstrap 3 Form Wrapper for [laravelcollective/html](https://github.com/laravelcollective/html).
* [riskihajar/terbilang](https://github.com/riskihajar/terbilang), create indonesian in-word number (for payment receipt) and roman numeral. * [riskihajar/terbilang](https://github.com/riskihajar/terbilang), create indonesian in-word number (for payment receipt) and roman numeral.

16
app/Entities/Projects/JobsRepository.php

@ -49,13 +49,15 @@ class JobsRepository extends BaseRepository
$newJob->project_id = $projectId; $newJob->project_id = $projectId;
$newJob->save(); $newJob->save();
$selectedTasks = $job->tasks()->whereIn('id', $jobsData[$job->id.'_task_ids'])->get();
foreach ($selectedTasks as $task) {
$newTask = $task->replicate();
$newTask->progress = 0;
$newTask->job_id = $newJob->id;
$newTask->save();
if (isset($jobsData[$job->id.'_task_ids'])) {
$selectedTasks = $job->tasks()->whereIn('id', $jobsData[$job->id.'_task_ids'])->get();
foreach ($selectedTasks as $task) {
$newTask = $task->replicate();
$newTask->progress = 0;
$newTask->job_id = $newJob->id;
$newTask->save();
}
} }
} }
DB::commit(); DB::commit();

3
app/Http/Controllers/Auth/ResetPasswordController.php

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use Illuminate\Support\Str;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords; use Illuminate\Foundation\Auth\ResetsPasswords;
@ -42,7 +43,7 @@ class ResetPasswordController extends Controller
{ {
$user->forceFill([ $user->forceFill([
'password' => $password, 'password' => $password,
'remember_token' => str_random(60),
'remember_token' => Str::random(60),
])->save(); ])->save();
$this->guard()->login($user); $this->guard()->login($user);

3
app/Http/Controllers/InstallationController.php

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use DB; use DB;
use Auth; use Auth;
use Illuminate\Support\Str;
use App\Entities\Users\User; use App\Entities\Users\User;
use App\Http\Requests\Accounts\RegisterRequest; use App\Http\Requests\Accounts\RegisterRequest;
@ -47,7 +48,7 @@ class InstallationController extends Controller
$adminData = $request->only('name', 'email', 'password'); $adminData = $request->only('name', 'email', 'password');
$adminData['api_token'] = str_random(32);
$adminData['api_token'] = Str::random(32);
$adminData['password'] = bcrypt($adminData['password']); $adminData['password'] = bcrypt($adminData['password']);
$admin = User::create($adminData); $admin = User::create($adminData);

2
app/Http/Controllers/References/SiteOptionsController.php

@ -15,7 +15,7 @@ class SiteOptionsController extends Controller
{ {
public function page1() public function page1()
{ {
return view('options.page-1', compact('options'));
return view('options.page-1');
} }
public function save1(Request $request) public function save1(Request $request)

3
app/Http/Controllers/Users/UsersController.php

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Users; namespace App\Http\Controllers\Users;
use Illuminate\Support\Str;
use App\Entities\Users\User; use App\Entities\Users\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -43,7 +44,7 @@ class UsersController extends Controller
$userData['password'] = bcrypt(\Option::get('password_default', 'member')); $userData['password'] = bcrypt(\Option::get('password_default', 'member'));
} }
$userData['api_token'] = str_random(32);
$userData['api_token'] = Str::random(32);
$user = User::create($userData); $user = User::create($userData);

3
app/Services/InvoiceDraft/InvoiceDraftCollection.php

@ -2,6 +2,7 @@
namespace App\Services\InvoiceDrafts; namespace App\Services\InvoiceDrafts;
use Illuminate\Support\Str;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@ -68,7 +69,7 @@ class InvoiceDraftCollection
public function add(InvoiceDraft $draft) public function add(InvoiceDraft $draft)
{ {
$content = $this->getContent(); $content = $this->getContent();
$draft->draftKey = str_random(10);
$draft->draftKey = Str::random(10);
$content->put($draft->draftKey, $draft); $content->put($draft->draftKey, $draft);
$this->session->put($this->instance, $content); $this->session->put($this->instance, $content);

2
composer.json

@ -22,7 +22,7 @@
"doctrine/dbal": "^2.9", "doctrine/dbal": "^2.9",
"fzaninotto/faker": "^1.4", "fzaninotto/faker": "^1.4",
"johnkary/phpunit-speedtrap": "^3.0", "johnkary/phpunit-speedtrap": "^3.0",
"luthfi/simple-crud-generator": "1.2.*",
"luthfi/simple-crud-generator": "^1.2",
"mockery/mockery": "^1.0", "mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0" "phpunit/phpunit": "^7.0"
}, },

1871
composer.lock
File diff suppressed because it is too large
View File

5
database/factories/BankAccountFactory.php

@ -1,12 +1,13 @@
<?php <?php
use Illuminate\Support\Str;
use Faker\Generator as Faker; use Faker\Generator as Faker;
use App\Entities\Invoices\BankAccount; use App\Entities\Invoices\BankAccount;
$factory->define(BankAccount::class, function (Faker $faker) { $factory->define(BankAccount::class, function (Faker $faker) {
return [ return [
'name' => 'Bank '.strtoupper(str_random(4)),
'number' => str_random(10),
'name' => 'Bank '.strtoupper(Str::random(4)),
'number' => Str::random(10),
'account_name' => $faker->name, 'account_name' => $faker->name,
]; ];
}); });

5
database/factories/ModelFactory.php

@ -1,5 +1,6 @@
<?php <?php
use Illuminate\Support\Str;
use App\Entities\Users\User; use App\Entities\Users\User;
use App\Entities\Users\Event; use App\Entities\Users\Event;
use App\Entities\Projects\Job; use App\Entities\Projects\Job;
@ -11,8 +12,8 @@ $factory->define(User::class, function (Faker\Generator $faker) {
'name' => $faker->name, 'name' => $faker->name,
'email' => $faker->unique()->email, 'email' => $faker->unique()->email,
'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
'remember_token' => str_random(10),
'api_token' => str_random(32),
'remember_token' => Str::random(10),
'api_token' => Str::random(32),
'lang' => 'en', 'lang' => 'en',
]; ];
}); });

7
database/factories/SubscriptionFactory.php

@ -1,6 +1,7 @@
<?php <?php
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Str;
use Faker\Generator as Faker; use Faker\Generator as Faker;
use App\Entities\Partners\Vendor; use App\Entities\Partners\Vendor;
use App\Entities\Projects\Project; use App\Entities\Projects\Project;
@ -11,19 +12,19 @@ $factory->define(Subscription::class, function (Faker $faker) {
$startDate = Carbon::parse($faker->dateTimeBetween('-1 year', '-1 month')->format('Y-m-d')); $startDate = Carbon::parse($faker->dateTimeBetween('-1 year', '-1 month')->format('Y-m-d'));
return [ return [
'project_id' => function () {
'project_id' => function () {
return factory(Project::class)->create()->id; return factory(Project::class)->create()->id;
}, },
'type_id' => 1, 'type_id' => 1,
'status_id' => 1, 'status_id' => 1,
'name' => 'www.'.str_random(10).'.com',
'name' => 'www.'.Str::random(10).'.com',
'price' => 125000, 'price' => 125000,
'start_date' => $startDate->format('Y-m-d'), 'start_date' => $startDate->format('Y-m-d'),
'due_date' => $startDate->addYears(1)->format('Y-m-d'), 'due_date' => $startDate->addYears(1)->format('Y-m-d'),
'customer_id' => function () { 'customer_id' => function () {
return factory(Customer::class)->create()->id; return factory(Customer::class)->create()->id;
}, },
'vendor_id' => function () {
'vendor_id' => function () {
return factory(Vendor::class)->create()->id; return factory(Vendor::class)->create()->id;
}, },
]; ];

2
resources/views/invoice-drafts/partials/form-draft-detail.blade.php

@ -21,7 +21,7 @@
{!! FormField::text('date', [ {!! FormField::text('date', [
'placeholder' => 'yyyy-mm-dd', 'placeholder' => 'yyyy-mm-dd',
'label' => trans('invoice.date'), 'label' => trans('invoice.date'),
'value' => $draft->date,
'value' => $draft->date ?: now()->format('Y-m-d'),
]) !!} ]) !!}
</div> </div>
<div class="col-md-6"> <div class="col-md-6">

3
resources/views/invoices/partials/detail.blade.php

@ -1,6 +1,6 @@
<table class="table"> <table class="table">
<tbody> <tbody>
<tr><th>{{ trans('invoice.number') }}</th><td class="text-primary strong">{{ $invoice->number }}</td></tr>
<tr><th class="col-md-4">{{ trans('invoice.number') }}</th><td class="text-primary strong">{{ $invoice->number }}</td></tr>
<tr><th>{{ trans('invoice.date') }}</th><td>{{ $invoice->date }}</td></tr> <tr><th>{{ trans('invoice.date') }}</th><td>{{ $invoice->date }}</td></tr>
<tr><th>{{ trans('invoice.due_date') }}</th><td>{{ $invoice->due_date }}</td></tr> <tr><th>{{ trans('invoice.due_date') }}</th><td>{{ $invoice->due_date }}</td></tr>
<tr><th>{{ trans('invoice.project') }}</th><td>{{ $invoice->project->nameLink() }}</td></tr> <tr><th>{{ trans('invoice.project') }}</th><td>{{ $invoice->project->nameLink() }}</td></tr>
@ -8,5 +8,6 @@
<tr><th>{{ trans('invoice.items_count') }}</th><td>{{ $invoice->items_count }}</td></tr> <tr><th>{{ trans('invoice.items_count') }}</th><td>{{ $invoice->items_count }}</td></tr>
<tr><th>{{ trans('invoice.creator') }}</th><td>{{ $invoice->creator->name }}</td></tr> <tr><th>{{ trans('invoice.creator') }}</th><td>{{ $invoice->creator->name }}</td></tr>
<tr><th>{{ trans('invoice.amount') }}</th><td class="text-right lead">{{ format_money($invoice->amount) }}</td></tr> <tr><th>{{ trans('invoice.amount') }}</th><td class="text-right lead">{{ format_money($invoice->amount) }}</td></tr>
<tr><th>{{ trans('invoice.notes') }}</th><td>{{ $invoice->notes }}</td></tr>
</tbody> </tbody>
</table> </table>

2
resources/views/payments/create.blade.php

@ -25,7 +25,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
{!! FormField::text('date', ['label'=> __('payment.date')]) !!}
{!! FormField::text('date', ['label'=> __('payment.date'), 'value' => now()->format('Y-m-d')]) !!}
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
{!! FormField::price('amount', ['label'=> __('payment.amount'), 'currency' => Option::get('money_sign', 'Rp')]) !!} {!! FormField::price('amount', ['label'=> __('payment.amount'), 'currency' => Option::get('money_sign', 'Rp')]) !!}

22
resources/views/projects/jobs/add-from-other-project.blade.php

@ -75,9 +75,31 @@
@if ($selectedProject) @if ($selectedProject)
@foreach ($selectedProject->jobs as $job) @foreach ($selectedProject->jobs as $job)
$('#job_id_{{ $job->id }}').change(function () { $('#job_id_{{ $job->id }}').change(function () {
$('.job_id_{{ $job->id }}_tasks').prop('checked', this.checked); $('.job_id_{{ $job->id }}_tasks').prop('checked', this.checked);
}); });
@foreach($job->tasks as $task)
$('#{{ $job->id }}_task_id_{{ $task->id }}').change(function () {
var condition = false;
$.each($(".job_id_{{ $job->id }}_tasks"), function( key, value ) {
if(value.checked == true){
condition = true
}
});
if(condition == true){
$('#job_id_{{ $job->id }}').prop('checked', true);
}
});
@endforeach
@endforeach @endforeach
@endif @endif
})(); })();

4
resources/views/projects/payments.blade.php

@ -7,8 +7,8 @@
<h1 class="page-header"> <h1 class="page-header">
<div class="pull-right"> <div class="pull-right">
{!! html_link_to_route('payments.create', trans('payment.create'), ['project_id' => $project->id, 'customer_id' => $project->customer_id], ['class' => 'btn btn-success', 'icon' => 'plus']) !!}
{!! html_link_to_route('projects.fees.create', trans('payment.create_fee'), ['project_id' => $project->id], ['class' => 'btn btn-default', 'icon' => 'plus']) !!}
{!! html_link_to_route('payments.create', trans('payment.create'), ['project_id' => $project, 'customer_id' => $project->customer_id], ['class' => 'btn btn-success', 'icon' => 'plus']) !!}
{!! html_link_to_route('projects.fees.create', trans('payment.create_fee'), $project, ['class' => 'btn btn-default', 'icon' => 'plus']) !!}
</div> </div>
{{ $project->name }} <small>{{ trans('project.payments') }}</small> {{ $project->name }} <small>{{ trans('project.payments') }}</small>
</h1> </h1>

32
tests/Feature/ManageJobsTest.php

@ -242,4 +242,36 @@ class ManageJobsTest extends TestCase
'id' => $task->id, 'id' => $task->id,
]); ]);
} }
/** @test */
public function admin_can_clone_jobs_without_tasks()
{
$user = $this->adminUserSigningIn();
$customer = factory(Customer::class)->create();
$projects = factory(Project::class, 2)->create(['customer_id' => $customer->id]);
$jobs = factory(Job::class, 3)->create(['project_id' => $projects[0]->id]);
$tasks1 = factory(Task::class, 3)->create(['job_id' => $jobs[0]->id]);
$tasks2 = factory(Task::class, 3)->create(['job_id' => $jobs[1]->id]);
$this->visitRoute('projects.jobs.add-from-other-project', [$projects[1]->id, 'project_id' => $projects[0]->id]);
$this->submitForm(trans('job.add'), [
'job_ids['.$jobs[0]->id.']' => $jobs[0]->id,
'job_ids['.$jobs[1]->id.']' => $jobs[1]->id,
]);
$this->seeInDatabase('jobs', [
'name' => $jobs[0]->name,
'price' => $jobs[0]->price,
'project_id' => $projects[1]->id,
'worker_id' => $jobs[0]->worker_id,
]);
$this->seeInDatabase('jobs', [
'name' => $jobs[1]->name,
'price' => $jobs[1]->price,
'project_id' => $projects[1]->id,
'worker_id' => $jobs[1]->worker_id,
]);
}
} }

36
tests/Feature/Payments/ManagePaymentsTest.php

@ -220,4 +220,40 @@ class ManagePaymentsTest extends TestCase
$this->see($payment->description); $this->see($payment->description);
$this->see($payment->partner->name); $this->see($payment->partner->name);
} }
/** @test */
public function admin_can_entry_payment_from_project_payment_tab()
{
$user = $this->adminUserSigningIn();
$project = factory(Project::class)->create();
$this->visitRoute('projects.payments', $project->id);
$this->click(trans('payment.create'));
$this->seeRouteIs('payments.create', ['customer_id' => $project->customer_id, 'project_id' => $project->id]);
// // Fill Form
$this->submitForm(trans('payment.create'), [
'date' => '2015-05-01',
'in_out' => 1,
'type_id' => 1,
'amount' => 1000000,
'project_id' => $project->id,
'partner_id' => $project->customer_id,
'description' => 'Pembayaran DP',
]);
$this->see(trans('payment.created'));
$this->seeRouteIs('projects.payments', $project->id);
$this->seeInDatabase('payments', [
'project_id' => $project->id,
'amount' => 1000000,
'type_id' => 1,
'in_out' => 1,
'date' => '2015-05-01',
'partner_type' => Customer::class,
'partner_id' => $project->customer_id,
]);
}
} }
Loading…
Cancel
Save