diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 3edff381e7de..4b5552237a4d 100755 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -7,6 +7,7 @@ use App\Http\Requests\TaskRequest; use App\Http\Requests\UpdateTaskRequest; use App\Models\Client; use App\Models\Project; +use App\Models\Product; use App\Models\Task; use App\Models\TaskStatus; use App\Ninja\Datatables\TaskDatatable; @@ -128,6 +129,7 @@ class TaskController extends BaseController 'task' => null, 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), 'projectPublicId' => Input::old('project_id') ? Input::old('project_id') : ($request->project_id ?: 0), + 'productPublicId' => Input::old('product_id') ? Input::old('product_id') : ($request->product_id ?: 0), 'method' => 'POST', 'url' => 'tasks', 'title' => trans('texts.new_task'), @@ -205,6 +207,7 @@ class TaskController extends BaseController 'entity' => $task, 'clientPublicId' => $task->client ? $task->client->public_id : 0, 'projectPublicId' => $task->project ? $task->project->public_id : 0, + 'productPublicId' => $task->product ? $task->product->public_id : 0, 'method' => $method, 'url' => $url, 'title' => trans('texts.edit_task'), @@ -241,6 +244,7 @@ class TaskController extends BaseController 'clients' => Client::scope()->withActiveOrSelected($task ? $task->client_id : false)->with('contacts')->orderBy('name')->get(), 'account' => Auth::user()->account, 'projects' => Project::scope()->withActiveOrSelected($task ? $task->project_id : false)->with('client.contacts')->orderBy('name')->get(), + 'products' => Product::scope()->withActiveOrSelected($task ? $task->product_id : false)->orderBy('product_key')->get(), ]; } @@ -330,12 +334,20 @@ class TaskController extends BaseController $account = Auth::user()->account; $showProject = $lastProjectId != $task->project_id; - $data[] = [ + $item_data = [ 'publicId' => $task->public_id, 'description' => $task->present()->invoiceDescription($account, $showProject), 'duration' => $task->getHours(), 'cost' => $task->getRate(), + 'productKey' => null, ]; + + if (!empty($task->product_id)) { + $item_data['productKey'] = $task->product->product_key; + } + + $data[] = $item_data; + $lastProjectId = $task->project_id; } diff --git a/app/Models/Task.php b/app/Models/Task.php index d695abe0fdd1..c2e5c88535b4 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -81,6 +81,14 @@ class Task extends EntityModel return $this->belongsTo('App\Models\Project')->withTrashed(); } + /** + * @return mixed + */ + public function product() + { + return $this->belongsTo('App\Models\Product')->withTrashed(); + } + /** * @return mixed */ @@ -180,7 +188,9 @@ class Task extends EntityModel { $value = 0; - if ($this->project && floatval($this->project->task_rate)) { + if ($this->product && $this->product->cost) { + $value = $this->product->cost; + } elseif ($this->project && floatval($this->project->task_rate)) { $value = $this->project->task_rate; } elseif ($this->client && floatval($this->client->task_rate)) { $value = $this->client->task_rate; diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index 08fb09e79ad6..be17a64a59a9 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -4,6 +4,7 @@ namespace App\Ninja\Repositories; use App\Models\Client; use App\Models\Project; +use App\Models\Product; use App\Models\Task; use App\Models\TaskStatus; use Auth; @@ -25,6 +26,7 @@ class TaskRepository extends BaseRepository ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id') ->leftJoin('invoices', 'invoices.id', '=', 'tasks.invoice_id') ->leftJoin('projects', 'projects.id', '=', 'tasks.project_id') + ->leftJoin('products', 'products.id', '=', 'tasks.product_id') ->leftJoin('task_statuses', 'task_statuses.id', '=', 'tasks.task_status_id') ->where('tasks.account_id', '=', Auth::user()->account_id) ->where(function ($query) { // handle when client isn't set @@ -173,6 +175,11 @@ class TaskRepository extends BaseRepository } } + if (!empty($data['product_id'])) { + $product = Product::scope($data['product_id'])->firstOrFail(); + $task->product_id = $product->id; + } + if (isset($data['description'])) { $task->description = trim($data['description']); } diff --git a/database/migrations/2020_01_11_215020_add_task_products.php b/database/migrations/2020_01_11_215020_add_task_products.php new file mode 100644 index 000000000000..54eb0e007a66 --- /dev/null +++ b/database/migrations/2020_01_11_215020_add_task_products.php @@ -0,0 +1,32 @@ +integer('product_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tasks', function ($table) { + $table->dropColumn('product_id'); + }); + } +} diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 6b9c189e7858..c10faa3779fd 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -936,7 +936,8 @@ item.notes(task.description); item.qty(task.duration); item.cost(task.cost); - item.task_public_id(task.publicId); + item.task_public_id(task.publicId); + item.product_key(task.productKey); } model.invoice().has_tasks(true); NINJA.formIsChanged = true; diff --git a/resources/views/tasks/edit.blade.php b/resources/views/tasks/edit.blade.php index 014753e7d1c9..0183de5a8456 100644 --- a/resources/views/tasks/edit.blade.php +++ b/resources/views/tasks/edit.blade.php @@ -58,12 +58,23 @@ ->label('project') ->value($task->present()->project) !!} @endif + + @if ($task->product) + {!! Former::plaintext() + ->label('product') + ->value($task->present()->product) !!} + @endif @else {!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!} {!! Former::select('project_id') ->addOption('', '') ->addGroupClass('project-select') ->label(trans('texts.project')) !!} + + {!! Former::select('product_id') + ->addOption('', '') + ->addGroupClass('product-select') + ->label(trans('texts.product')) !!} @endif @include('partials/custom_fields', ['entityType' => ENTITY_TASK]) @@ -240,6 +251,7 @@ var clients = {!! $clients !!}; var projects = {!! $projects !!}; + var products = {!! $products !!}; var timeLabels = {}; @foreach (['hour', 'minute', 'second'] as $period) @@ -552,9 +564,11 @@ // setup clients and project comboboxes var clientId = {{ $clientPublicId }}; var projectId = {{ $projectPublicId }}; + var productId = {{ $productPublicId }}; var clientMap = {}; var projectMap = {}; + var productMap = {}; var projectsForClientMap = {}; var projectsForAllClients = []; var $clientSelect = $('select#client'); @@ -638,7 +652,33 @@ } }); + var $productSelect = $('select#product_id').on('change', function(e) { + var productId = $('input[name=product_id]').val(); + if (productId == '-1') { + $('input[name=product_name]').val(''); + } + + $('select#project_id').combobox('refresh'); + }); + + $productSelect.append(new Option('', '')); + + for (var i=0; i ENTITY_PROJECT]) + @include('partials/entity_combobox', ['entityType' => ENTITY_PRODUCT]) if (projectId) { var project = projectMap[projectId]; @@ -650,6 +690,14 @@ $clientSelect.trigger('change'); } + if (productId) { + var product = productMap[productId]; + if (product) { + setComboboxValue($('.product-select'), product.public_id, product.product_key); + $productSelect.trigger('change'); + } + } + @if (!$task) var taskType = localStorage.getItem('last:task_type'); if (taskType) {