diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php
index 16dc60adad5a..4091667352e4 100644
--- a/app/Http/Controllers/TaskController.php
+++ b/app/Http/Controllers/TaskController.php
@@ -262,7 +262,7 @@ class TaskController extends BaseController
$this->taskRepo->save($ids, ['action' => $action]);
return Redirect::to('tasks')->withMessage(trans($action == 'stop' ? 'texts.stopped_task' : 'texts.resumed_task'));
} elseif ($action == 'invoice' || $action == 'add_to_invoice') {
- $tasks = Task::scope($ids)->with('client')->orderBy('project_id', 'id')->get();
+ $tasks = Task::scope($ids)->with('account', 'client', 'project')->orderBy('project_id', 'id')->get();
$clientPublicId = false;
$data = [];
@@ -294,6 +294,7 @@ class TaskController extends BaseController
'publicId' => $task->public_id,
'description' => $task->present()->invoiceDescription($account, $showProject),
'duration' => $task->getHours(),
+ 'cost' => $task->getRate(),
];
$lastProjectId = $task->project_id;
}
diff --git a/app/Models/Account.php b/app/Models/Account.php
index 2f28c19a3d59..db5ec2968a81 100644
--- a/app/Models/Account.php
+++ b/app/Models/Account.php
@@ -176,6 +176,7 @@ class Account extends Eloquent
'credit_number_counter',
'credit_number_prefix',
'credit_number_pattern',
+ 'task_rate',
];
/**
diff --git a/app/Models/Client.php b/app/Models/Client.php
index 4ff71cee43f0..3213e3d17026 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -52,6 +52,7 @@ class Client extends EntityModel
'invoice_number_counter',
'quote_number_counter',
'public_notes',
+ 'task_rate',
];
diff --git a/app/Models/Project.php b/app/Models/Project.php
index 834aef98b00e..72bd1d10c1b7 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -24,6 +24,7 @@ class Project extends EntityModel
*/
protected $fillable = [
'name',
+ 'task_rate',
];
/**
diff --git a/app/Models/Task.php b/app/Models/Task.php
index 139627f46a24..ddf7dc67ff7e 100644
--- a/app/Models/Task.php
+++ b/app/Models/Task.php
@@ -145,6 +145,24 @@ class Task extends EntityModel
return self::calcDuration($this);
}
+ /**
+ * @return float
+ */
+ public function getRate()
+ {
+ $value = 0;
+
+ if ($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;
+ } else {
+ $value = $this->account->task_rate;
+ }
+
+ return Utils::roundSignificant($value);
+ }
+
/**
* @return int
*/
diff --git a/app/Ninja/Datatables/ProjectDatatable.php b/app/Ninja/Datatables/ProjectDatatable.php
index 2235481ad83e..6165028e45aa 100644
--- a/app/Ninja/Datatables/ProjectDatatable.php
+++ b/app/Ninja/Datatables/ProjectDatatable.php
@@ -38,6 +38,12 @@ class ProjectDatatable extends EntityDatatable
}
},
],
+ [
+ 'task_rate',
+ function ($model) {
+ return floatval($model->task_rate) ? Utils::roundSignificant($model->task_rate) : '';
+ }
+ ],
];
}
diff --git a/app/Ninja/Repositories/ProjectRepository.php b/app/Ninja/Repositories/ProjectRepository.php
index 8e1bedb5b14e..8aa704efca71 100644
--- a/app/Ninja/Repositories/ProjectRepository.php
+++ b/app/Ninja/Repositories/ProjectRepository.php
@@ -35,6 +35,7 @@ class ProjectRepository extends BaseRepository
'projects.public_id',
'projects.user_id',
'projects.deleted_at',
+ 'projects.task_rate',
'projects.is_deleted',
DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"),
'clients.user_id as client_user_id',
diff --git a/app/Ninja/Transformers/AccountTransformer.php b/app/Ninja/Transformers/AccountTransformer.php
index f34f466705c4..dbf53acf9485 100644
--- a/app/Ninja/Transformers/AccountTransformer.php
+++ b/app/Ninja/Transformers/AccountTransformer.php
@@ -274,6 +274,7 @@ class AccountTransformer extends EntityTransformer
'reset_counter_date' => $account->reset_counter_date,
'custom_contact_label1' => $account->custom_contact_label1,
'custom_contact_label2' => $account->custom_contact_label2,
+ 'task_rate' => (float) $account->task_rate,
];
}
}
diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php
index 6e1d6a23f662..e6814a287f5e 100644
--- a/app/Ninja/Transformers/ClientTransformer.php
+++ b/app/Ninja/Transformers/ClientTransformer.php
@@ -37,6 +37,7 @@ class ClientTransformer extends EntityTransformer
* @SWG\Property(property="vat_number", type="string", example="123456")
* @SWG\Property(property="id_number", type="string", example="123456")
* @SWG\Property(property="language_id", type="integer", example=1)
+ * @SWG\Property(property="task_rate", type="number", format="float", example=10)
*/
protected $defaultIncludes = [
'contacts',
@@ -135,6 +136,7 @@ class ClientTransformer extends EntityTransformer
'custom_value2' => $client->custom_value2,
'invoice_number_counter' => (int) $client->invoice_number_counter,
'quote_number_counter' => (int) $client->quote_number_counter,
+ 'task_rate' => (float) $client->task_rate,
]);
}
}
diff --git a/app/Ninja/Transformers/ProjectTransformer.php b/app/Ninja/Transformers/ProjectTransformer.php
index 6e6dbcb2bb84..dbff6390a40a 100644
--- a/app/Ninja/Transformers/ProjectTransformer.php
+++ b/app/Ninja/Transformers/ProjectTransformer.php
@@ -16,6 +16,7 @@ class ProjectTransformer extends EntityTransformer
* @SWG\Property(property="updated_at", type="integer", example=1451160233, readOnly=true)
* @SWG\Property(property="archived_at", type="integer", example=1451160233, readOnly=true)
* @SWG\Property(property="is_deleted", type="boolean", example=false, readOnly=true)
+ * @SWG\Property(property="task_rate", type="number", format="float", example=10)
*/
public function transform(Project $project)
{
@@ -26,6 +27,7 @@ class ProjectTransformer extends EntityTransformer
'updated_at' => $this->getTimestamp($project->updated_at),
'archived_at' => $this->getTimestamp($project->deleted_at),
'is_deleted' => (bool) $project->is_deleted,
+ 'task_rate' => (float) $project->task_rate,
]);
}
}
diff --git a/database/migrations/2017_10_17_083846_add_default_rates.php b/database/migrations/2017_10_17_083846_add_default_rates.php
new file mode 100644
index 000000000000..fbc3d4dffc53
--- /dev/null
+++ b/database/migrations/2017_10_17_083846_add_default_rates.php
@@ -0,0 +1,47 @@
+decimal('task_rate', 12, 4);
+ });
+
+ Schema::table('clients', function ($table) {
+ $table->decimal('task_rate', 12, 4);
+ });
+
+ Schema::table('projects', function ($table) {
+ $table->decimal('task_rate', 12, 4);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('accounts', function ($table) {
+ $table->dropColumn('task_rate');
+ });
+
+ Schema::table('clients', function ($table) {
+ $table->dropColumn('task_rate');
+ });
+
+ Schema::table('projects', function ($table) {
+ $table->dropColumn('task_rate');
+ });
+ }
+}
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 608a395b54b5..cf975ebd7c36 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -2483,6 +2483,9 @@ $LANG = array(
'clear' => 'Clear',
'warn_payment_gateway' => 'Note: to accept online payments :link to add a payment gateway.',
'setup_desktop_app' => 'Setup the desktop app',
+ 'task_rate' => 'Task Rate',
+ 'task_rate_help' => 'Set the default rate for invoiced tasks.',
+
);
return $LANG;
diff --git a/resources/views/accounts/details.blade.php b/resources/views/accounts/details.blade.php
index 30010c9c8ff8..44caa554b966 100644
--- a/resources/views/accounts/details.blade.php
+++ b/resources/views/accounts/details.blade.php
@@ -19,6 +19,7 @@
]) !!}
{{ Former::populate($account) }}
+ {{ Former::populateField('task_rate', floatval($account->task_rate) ? Utils::roundSignificant($account->task_rate) : '') }}
@include('accounts.nav', ['selected' => ACCOUNT_COMPANY_DETAILS])
@@ -99,6 +100,11 @@
->fromQuery(\App\Models\PaymentTerm::getSelectOptions(), 'name', 'num_days')
->help(trans('texts.payment_terms_help') . ' | ' . link_to('/settings/payment_terms', trans('texts.customize_options'))) !!}
+ @if ($account->isModuleEnabled(ENTITY_TASK))
+ {!! Former::text('task_rate')
+ ->help('task_rate_help')!!}
+ @endif
+
diff --git a/resources/views/clients/edit.blade.php b/resources/views/clients/edit.blade.php
index ad05536cf3c3..289793590564 100644
--- a/resources/views/clients/edit.blade.php
+++ b/resources/views/clients/edit.blade.php
@@ -23,6 +23,7 @@
@if ($client)
{!! Former::populate($client) !!}
+ {!! Former::populateField('task_rate', floatval($client->task_rate) ? Utils::roundSignificant($client->task_rate) : '') !!}
{!! Former::hidden('public_id') !!}
@else
{!! Former::populateField('invoice_number_counter', 1) !!}
@@ -155,6 +156,10 @@
->fromQuery(\App\Models\PaymentTerm::getSelectOptions(), 'name', 'num_days')
->placeholder($account->present()->paymentTerms)
->help(trans('texts.payment_terms_help')) !!}
+ @if ($account->isModuleEnabled(ENTITY_TASK))
+ {!! Former::text('task_rate')
+ ->help('task_rate_help') !!}
+ @endif
{!! Former::select('size_id')->addOption('','')
->fromQuery($sizes, 'name', 'id') !!}
{!! Former::select('industry_id')->addOption('','')
diff --git a/resources/views/clients/show.blade.php b/resources/views/clients/show.blade.php
index 29957aa5b68b..e26731d2bfac 100644
--- a/resources/views/clients/show.blade.php
+++ b/resources/views/clients/show.blade.php
@@ -111,6 +111,8 @@
{{ $client->country->name }}
@endif
+
+
@if ($client->account->custom_client_label1 && $client->custom_value1)
{{ $client->account->custom_client_label1 . ': ' . $client->custom_value1 }}
@endif
@@ -122,6 +124,10 @@
{{ $client->work_phone }}
@endif
+ @if (floatval($client->task_rate))
+
{{ trans('texts.task_rate') }}: {{ Utils::roundSignificant($client->task_rate) }}
+ @endif + @if ($client->public_notes){{ $client->public_notes }}
@endif diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index f96c1bf9321c..ffff300bba08 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -893,6 +893,7 @@ var item = model.invoice().addItem(true); item.notes(task.description); item.qty(task.duration); + item.cost(task.cost); item.task_public_id(task.publicId); } model.invoice().has_tasks(true); diff --git a/resources/views/invoices/knockout.blade.php b/resources/views/invoices/knockout.blade.php index 2fe3bf6b3202..95eeeed13e2f 100644 --- a/resources/views/invoices/knockout.blade.php +++ b/resources/views/invoices/knockout.blade.php @@ -1013,7 +1013,9 @@ ko.bindingHandlers.productTypeahead = { model.notes(datum.notes); } if (datum.cost) { - model.cost(roundSignificant(datum.cost, 2)); + if (! model.cost() || ! model.task_public_id()) { + model.cost(roundSignificant(datum.cost, 2)); + } } if (!model.qty()) { model.qty(1); diff --git a/resources/views/projects/edit.blade.php b/resources/views/projects/edit.blade.php index e30b119ca0f7..9407388a0894 100644 --- a/resources/views/projects/edit.blade.php +++ b/resources/views/projects/edit.blade.php @@ -12,6 +12,7 @@ @if ($project) {!! Former::populate($project) !!} + {!! Former::populateField('task_rate', floatval($project->task_rate) ? Utils::roundSignificant($project->task_rate) : '') !!} @endif