mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 20:52:56 +01:00
Added task projects
This commit is contained in:
parent
408aede80a
commit
bbbf1237c5
@ -75,7 +75,7 @@ class AccountApiController extends BaseAPIController
|
||||
$updatedAt = $request->updated_at ? date('Y-m-d H:i:s', $request->updated_at) : false;
|
||||
|
||||
$transformer = new AccountTransformer(null, $request->serializer);
|
||||
$account->load($transformer->getDefaultIncludes());
|
||||
$account->load(array_merge($transformer->getDefaultIncludes(), ['projects.client']));
|
||||
$account = $this->createItem($account, $transformer, 'account');
|
||||
|
||||
return $this->response($account);
|
||||
|
@ -44,7 +44,7 @@ class ExpenseApiController extends BaseAPIController
|
||||
{
|
||||
$expenses = Expense::scope()
|
||||
->withTrashed()
|
||||
->with('client', 'invoice', 'vendor')
|
||||
->with('client', 'invoice', 'vendor', 'expense_category')
|
||||
->orderBy('created_at','desc');
|
||||
|
||||
return $this->listResponse($expenses);
|
||||
|
@ -6,6 +6,7 @@ use Input;
|
||||
use Session;
|
||||
use App\Services\ExpenseCategoryService;
|
||||
use App\Ninja\Repositories\ExpenseCategoryRepository;
|
||||
use App\Ninja\Datatables\ExpenseCategoryDatatable;
|
||||
use App\Http\Requests\ExpenseCategoryRequest;
|
||||
use App\Http\Requests\CreateExpenseCategoryRequest;
|
||||
use App\Http\Requests\UpdateExpenseCategoryRequest;
|
||||
@ -29,14 +30,10 @@ class ExpenseCategoryController extends BaseController
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return View::make('list', [
|
||||
return View::make('list_wrapper', [
|
||||
'entityType' => ENTITY_EXPENSE_CATEGORY,
|
||||
'datatable' => new ExpenseCategoryDatatable(),
|
||||
'title' => trans('texts.expense_categories'),
|
||||
'columns' => Utils::trans([
|
||||
'checkbox',
|
||||
'name',
|
||||
''
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -77,7 +74,7 @@ class ExpenseCategoryController extends BaseController
|
||||
|
||||
Session::flash('message', trans('texts.created_expense_category'));
|
||||
|
||||
return redirect()->to($category->getRoute());
|
||||
return redirect()->to('/expense_categories');
|
||||
}
|
||||
|
||||
public function update(UpdateExpenseCategoryRequest $request)
|
||||
|
@ -252,7 +252,7 @@ class ExpenseController extends BaseController
|
||||
'countries' => Cache::get('countries'),
|
||||
'customLabel1' => Auth::user()->account->custom_vendor_label1,
|
||||
'customLabel2' => Auth::user()->account->custom_vendor_label2,
|
||||
'categories' => ExpenseCategory::whereAccountId(Auth::user()->account_id)->orderBy('name')->get(),
|
||||
'categories' => ExpenseCategory::whereAccountId(Auth::user()->account_id)->withArchived()->orderBy('name')->get(),
|
||||
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
|
||||
];
|
||||
}
|
||||
|
@ -155,7 +155,8 @@ class ProductController extends BaseController
|
||||
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
||||
$count = $this->productService->bulk($ids, $action);
|
||||
|
||||
Session::flash('message', trans('texts.archived_product'));
|
||||
$message = Utils::pluralize($action.'d_product', $count);
|
||||
Session::flash('message', $message);
|
||||
|
||||
return $this->returnBulk(ENTITY_PRODUCT, $action, $ids);
|
||||
}
|
||||
|
113
app/Http/Controllers/ProjectController.php
Normal file
113
app/Http/Controllers/ProjectController.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Auth;
|
||||
use View;
|
||||
use Utils;
|
||||
use Input;
|
||||
use Session;
|
||||
use App\Models\Client;
|
||||
use App\Services\ProjectService;
|
||||
use App\Ninja\Repositories\ProjectRepository;
|
||||
use App\Ninja\Datatables\ProjectDatatable;
|
||||
use App\Http\Requests\ProjectRequest;
|
||||
use App\Http\Requests\CreateProjectRequest;
|
||||
use App\Http\Requests\UpdateProjectRequest;
|
||||
|
||||
class ProjectController extends BaseController
|
||||
{
|
||||
protected $projectRepo;
|
||||
protected $projectService;
|
||||
protected $entityType = ENTITY_PROJECT;
|
||||
|
||||
public function __construct(ProjectRepository $projectRepo, ProjectService $projectService)
|
||||
{
|
||||
$this->projectRepo = $projectRepo;
|
||||
$this->projectService = $projectService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return View::make('list_wrapper', [
|
||||
'entityType' => ENTITY_PROJECT,
|
||||
'datatable' => new ProjectDatatable(),
|
||||
'title' => trans('texts.projects'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDatatable($expensePublicId = null)
|
||||
{
|
||||
$search = Input::get('sSearch');
|
||||
$userId = Auth::user()->filterId();
|
||||
|
||||
return $this->projectService->getDatatable($search, $userId);
|
||||
}
|
||||
|
||||
public function create(ProjectRequest $request)
|
||||
{
|
||||
$data = [
|
||||
'project' => null,
|
||||
'method' => 'POST',
|
||||
'url' => 'projects',
|
||||
'title' => trans('texts.new_project'),
|
||||
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
|
||||
'clientPublicId' => $request->client_id,
|
||||
];
|
||||
|
||||
return View::make('projects.edit', $data);
|
||||
}
|
||||
|
||||
public function edit(ProjectRequest $request)
|
||||
{
|
||||
$project = $request->entity();
|
||||
|
||||
$data = [
|
||||
'project' => $project,
|
||||
'method' => 'PUT',
|
||||
'url' => 'projects/' . $project->public_id,
|
||||
'title' => trans('texts.edit_project'),
|
||||
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
|
||||
'clientPublicId' => $project->client ? $project->client->public_id : null,
|
||||
];
|
||||
|
||||
return View::make('projects.edit', $data);
|
||||
}
|
||||
|
||||
public function store(CreateProjectRequest $request)
|
||||
{
|
||||
$project = $this->projectRepo->save($request->input());
|
||||
|
||||
Session::flash('message', trans('texts.created_project'));
|
||||
|
||||
return redirect()->to($project->getRoute());
|
||||
}
|
||||
|
||||
public function update(UpdateProjectRequest $request)
|
||||
{
|
||||
$project = $this->projectRepo->save($request->input(), $request->entity());
|
||||
|
||||
Session::flash('message', trans('texts.updated_project'));
|
||||
|
||||
return redirect()->to($project->getRoute());
|
||||
}
|
||||
|
||||
public function bulk()
|
||||
{
|
||||
$action = Input::get('action');
|
||||
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
||||
$count = $this->projectService->bulk($ids, $action);
|
||||
|
||||
if ($count > 0) {
|
||||
$field = $count == 1 ? "{$action}d_project" : "{$action}d_projects";
|
||||
$message = trans("texts.$field", ['count' => $count]);
|
||||
Session::flash('message', $message);
|
||||
}
|
||||
|
||||
return redirect()->to('/projects');
|
||||
}
|
||||
|
||||
}
|
@ -41,7 +41,7 @@ class TaskApiController extends BaseAPIController
|
||||
{
|
||||
$tasks = Task::scope()
|
||||
->withTrashed()
|
||||
->with('client', 'invoice')
|
||||
->with('client', 'invoice', 'project')
|
||||
->orderBy('created_at', 'desc');
|
||||
|
||||
return $this->listResponse($tasks);
|
||||
|
@ -10,6 +10,7 @@ use Session;
|
||||
use DropdownButton;
|
||||
use App\Models\Client;
|
||||
use App\Models\Task;
|
||||
use App\Models\Project;
|
||||
use App\Ninja\Repositories\TaskRepository;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Services\TaskService;
|
||||
@ -120,6 +121,7 @@ class TaskController extends BaseController
|
||||
$data = [
|
||||
'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),
|
||||
'method' => 'POST',
|
||||
'url' => 'tasks',
|
||||
'title' => trans('texts.new_task'),
|
||||
@ -171,6 +173,7 @@ class TaskController extends BaseController
|
||||
'task' => $task,
|
||||
'entity' => $task,
|
||||
'clientPublicId' => $task->client ? $task->client->public_id : 0,
|
||||
'projectPublicId' => $task->project ? $task->project->public_id : 0,
|
||||
'method' => 'PUT',
|
||||
'url' => 'tasks/'.$task->public_id,
|
||||
'title' => trans('texts.edit_task'),
|
||||
@ -206,6 +209,7 @@ class TaskController extends BaseController
|
||||
return [
|
||||
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
|
||||
'account' => Auth::user()->account,
|
||||
'projects' => Project::scope()->with('client.contacts')->withArchived()->orderBy('name')->get(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ class UserController extends BaseController
|
||||
Session::flash('message', $message);
|
||||
}
|
||||
|
||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
||||
return Redirect::to('users/' . $user->public_id . '/edit');
|
||||
}
|
||||
|
||||
public function sendConfirmation($userPublicId)
|
||||
|
26
app/Http/Requests/CreateProjectRequest.php
Normal file
26
app/Http/Requests/CreateProjectRequest.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
class CreateProjectRequest extends ProjectRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->can('create', ENTITY_PROJECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => sprintf('required|unique:projects,name,,id,account_id,%s', $this->user()->account_id),
|
||||
];
|
||||
}
|
||||
}
|
7
app/Http/Requests/ProjectRequest.php
Normal file
7
app/Http/Requests/ProjectRequest.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
class ProjectRequest extends EntityRequest {
|
||||
|
||||
protected $entityType = ENTITY_PROJECT;
|
||||
|
||||
}
|
27
app/Http/Requests/UpdateProjectRequest.php
Normal file
27
app/Http/Requests/UpdateProjectRequest.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
class UpdateProjectRequest extends ProjectRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->can('edit', $this->entity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'name' => sprintf('required|unique:projects,name,%s,id,account_id,%s', $this->entity()->id, $this->user()->account_id),
|
||||
];
|
||||
}
|
||||
}
|
@ -146,6 +146,13 @@ Route::group(['middleware' => 'auth:user'], function() {
|
||||
Route::get('api/tasks/{client_id?}', 'TaskController@getDatatable');
|
||||
Route::get('tasks/create/{client_id?}', 'TaskController@create');
|
||||
Route::post('tasks/bulk', 'TaskController@bulk');
|
||||
Route::get('projects', 'ProjectController@index');
|
||||
Route::get('api/projects', 'ProjectController@getDatatable');
|
||||
Route::get('projects/create/{client_id?}', 'ProjectController@create');
|
||||
Route::post('projects', 'ProjectController@store');
|
||||
Route::put('projects/{projects}', 'ProjectController@update');
|
||||
Route::get('projects/{projects}/edit', 'ProjectController@edit');
|
||||
Route::post('projects/bulk', 'ProjectController@bulk');
|
||||
|
||||
Route::get('api/recurring_invoices/{client_id?}', 'InvoiceController@getRecurringDatatable');
|
||||
|
||||
@ -388,6 +395,7 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('ENTITY_BANK_ACCOUNT', 'bank_account');
|
||||
define('ENTITY_BANK_SUBACCOUNT', 'bank_subaccount');
|
||||
define('ENTITY_EXPENSE_CATEGORY', 'expense_category');
|
||||
define('ENTITY_PROJECT', 'project');
|
||||
|
||||
define('INVOICE_TYPE_STANDARD', 1);
|
||||
define('INVOICE_TYPE_QUOTE', 2);
|
||||
|
@ -303,6 +303,14 @@ class Account extends Eloquent
|
||||
return $this->hasMany('App\Models\ExpenseCategory','account_id','id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function projects()
|
||||
{
|
||||
return $this->hasMany('App\Models\Project','account_id','id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*/
|
||||
|
@ -47,13 +47,4 @@ class ExpenseCategory extends EntityModel
|
||||
{
|
||||
return "/expense_categories/{$this->public_id}/edit";
|
||||
}
|
||||
|
||||
public static function getStates($entityType = false)
|
||||
{
|
||||
$statuses = parent::getStates($entityType);
|
||||
|
||||
unset($statuses[STATUS_DELETED]);
|
||||
|
||||
return $statuses;
|
||||
}
|
||||
}
|
||||
|
@ -87,13 +87,4 @@ class Product extends EntityModel
|
||||
{
|
||||
return $this->belongsTo('App\Models\TaxRate');
|
||||
}
|
||||
|
||||
public static function getStates($entityType = false)
|
||||
{
|
||||
$statuses = parent::getStates($entityType);
|
||||
|
||||
unset($statuses[STATUS_DELETED]);
|
||||
|
||||
return $statuses;
|
||||
}
|
||||
}
|
||||
|
65
app/Models/Project.php
Normal file
65
app/Models/Project.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
|
||||
/**
|
||||
* Class ExpenseCategory
|
||||
*/
|
||||
class Project extends EntityModel
|
||||
{
|
||||
// Expense Categories
|
||||
use SoftDeletes;
|
||||
use PresentableTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'client_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $presenter = 'App\Ninja\Presenters\EntityPresenter';
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEntityType()
|
||||
{
|
||||
return ENTITY_PROJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRoute()
|
||||
{
|
||||
return "/projects/{$this->public_id}/edit";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function client()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Client')->withTrashed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Project::creating(function ($project) {
|
||||
$project->setNullValues();
|
||||
});
|
||||
|
||||
Project::updating(function ($project) {
|
||||
$project->setNullValues();
|
||||
});
|
@ -69,6 +69,14 @@ class Task extends EntityModel
|
||||
return $this->belongsTo('App\Models\Client')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Project')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $task
|
||||
* @return string
|
||||
|
59
app/Ninja/Datatables/ProjectDatatable.php
Normal file
59
app/Ninja/Datatables/ProjectDatatable.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php namespace App\Ninja\Datatables;
|
||||
|
||||
use Utils;
|
||||
use URL;
|
||||
use Auth;
|
||||
|
||||
class ProjectDatatable extends EntityDatatable
|
||||
{
|
||||
public $entityType = ENTITY_PROJECT;
|
||||
public $sortCol = 1;
|
||||
|
||||
public function columns()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'project',
|
||||
function ($model)
|
||||
{
|
||||
if ( ! Auth::user()->can('editByOwner', [ENTITY_PROJECT, $model->user_id])) {
|
||||
return $model->project;
|
||||
}
|
||||
|
||||
return link_to("projects/{$model->public_id}/edit", $model->project)->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
'client_name',
|
||||
function ($model)
|
||||
{
|
||||
if ($model->client_public_id) {
|
||||
if(!Auth::user()->can('viewByOwner', [ENTITY_CLIENT, $model->client_user_id])){
|
||||
return Utils::getClientDisplayName($model);
|
||||
}
|
||||
|
||||
return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model))->toHtml();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function actions()
|
||||
{
|
||||
return [
|
||||
[
|
||||
trans('texts.edit_project'),
|
||||
function ($model) {
|
||||
return URL::to("projects/{$model->public_id}/edit") ;
|
||||
},
|
||||
function ($model) {
|
||||
return Auth::user()->can('editByOwner', [ENTITY_PROJECT, $model->user_id]);
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -8,7 +8,7 @@ use App\Models\Task;
|
||||
class TaskDatatable extends EntityDatatable
|
||||
{
|
||||
public $entityType = ENTITY_TASK;
|
||||
public $sortCol = 2;
|
||||
public $sortCol = 3;
|
||||
|
||||
public function columns()
|
||||
{
|
||||
@ -24,6 +24,16 @@ class TaskDatatable extends EntityDatatable
|
||||
},
|
||||
! $this->hideClient
|
||||
],
|
||||
[
|
||||
'project',
|
||||
function ($model) {
|
||||
if(!Auth::user()->can('editByOwner', [ENTITY_PROJECT, $model->project_user_id])){
|
||||
return $model->project;
|
||||
}
|
||||
|
||||
return $model->project_public_id ? link_to("projects/{$model->project_public_id}/edit", $model->project)->toHtml() : '';
|
||||
}
|
||||
],
|
||||
[
|
||||
'date',
|
||||
function ($model) {
|
||||
|
@ -187,7 +187,8 @@ class AccountRepository
|
||||
ENTITY_VENDOR,
|
||||
ENTITY_RECURRING_INVOICE,
|
||||
ENTITY_PAYMENT,
|
||||
ENTITY_CREDIT
|
||||
ENTITY_CREDIT,
|
||||
ENTITY_PROJECT,
|
||||
];
|
||||
|
||||
foreach ($entityTypes as $entityType) {
|
||||
|
@ -25,7 +25,8 @@ class ExpenseCategoryRepository extends BaseRepository
|
||||
'expense_categories.name as category',
|
||||
'expense_categories.public_id',
|
||||
'expense_categories.user_id',
|
||||
'expense_categories.deleted_at'
|
||||
'expense_categories.deleted_at',
|
||||
'expense_categories.is_deleted'
|
||||
);
|
||||
|
||||
$this->applyFilters($query, ENTITY_EXPENSE_CATEGORY);
|
||||
|
@ -54,7 +54,7 @@ class ExpenseRepository extends BaseRepository
|
||||
->where('contacts.deleted_at', '=', null)
|
||||
->where('vendors.deleted_at', '=', null)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where(function ($query) {
|
||||
->where(function ($query) { // handle when client isn't set
|
||||
$query->where('contacts.is_primary', '=', true)
|
||||
->orWhere('contacts.is_primary', '=', null);
|
||||
})
|
||||
|
@ -32,7 +32,8 @@ class ProductRepository extends BaseRepository
|
||||
'products.cost',
|
||||
'tax_rates.name as tax_name',
|
||||
'tax_rates.rate as tax_rate',
|
||||
'products.deleted_at'
|
||||
'products.deleted_at',
|
||||
'products.is_deleted'
|
||||
);
|
||||
|
||||
if ($filter) {
|
||||
|
75
app/Ninja/Repositories/ProjectRepository.php
Normal file
75
app/Ninja/Repositories/ProjectRepository.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php namespace App\Ninja\Repositories;
|
||||
|
||||
use DB;
|
||||
use Utils;
|
||||
use Auth;
|
||||
use App\Models\Project;
|
||||
|
||||
class ProjectRepository extends BaseRepository
|
||||
{
|
||||
public function getClassName()
|
||||
{
|
||||
return 'App\Models\Project';
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
return Project::scope()->get();
|
||||
}
|
||||
|
||||
public function find($filter = null, $userId = false)
|
||||
{
|
||||
$query = DB::table('projects')
|
||||
->where('projects.account_id', '=', Auth::user()->account_id)
|
||||
->leftjoin('clients', 'clients.id', '=', 'projects.client_id')
|
||||
->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->where('contacts.deleted_at', '=', null)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where(function ($query) { // handle when client isn't set
|
||||
$query->where('contacts.is_primary', '=', true)
|
||||
->orWhere('contacts.is_primary', '=', null);
|
||||
})
|
||||
->select(
|
||||
'projects.name as project',
|
||||
'projects.public_id',
|
||||
'projects.user_id',
|
||||
'projects.deleted_at',
|
||||
'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',
|
||||
'clients.public_id as client_public_id'
|
||||
);
|
||||
|
||||
$this->applyFilters($query, ENTITY_PROJECT);
|
||||
|
||||
if ($filter) {
|
||||
$query->where(function ($query) use ($filter) {
|
||||
$query->where('clients.name', 'like', '%'.$filter.'%')
|
||||
->orWhere('contacts.first_name', 'like', '%'.$filter.'%')
|
||||
->orWhere('contacts.last_name', 'like', '%'.$filter.'%')
|
||||
->orWhere('contacts.email', 'like', '%'.$filter.'%')
|
||||
->orWhere('projects.name', 'like', '%'.$filter.'%');
|
||||
});
|
||||
}
|
||||
|
||||
if ($userId) {
|
||||
$query->where('projects.user_id', '=', $userId);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function save($input, $project = false)
|
||||
{
|
||||
$publicId = isset($data['public_id']) ? $data['public_id'] : false;
|
||||
|
||||
if ( ! $project) {
|
||||
$project = Project::createNew();
|
||||
}
|
||||
|
||||
$project->fill($input);
|
||||
$project->save();
|
||||
|
||||
return $project;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
use Auth;
|
||||
use Session;
|
||||
use App\Models\Client;
|
||||
use App\Models\Project;
|
||||
use App\Models\Task;
|
||||
|
||||
class TaskRepository extends BaseRepository
|
||||
@ -18,8 +19,9 @@ class TaskRepository extends BaseRepository
|
||||
->leftJoin('clients', 'tasks.client_id', '=', 'clients.id')
|
||||
->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->leftJoin('invoices', 'invoices.id', '=', 'tasks.invoice_id')
|
||||
->leftJoin('projects', 'projects.id', '=', 'tasks.project_id')
|
||||
->where('tasks.account_id', '=', Auth::user()->account_id)
|
||||
->where(function ($query) {
|
||||
->where(function ($query) { // handle when client isn't set
|
||||
$query->where('contacts.is_primary', '=', true)
|
||||
->orWhere('contacts.is_primary', '=', null);
|
||||
})
|
||||
@ -46,7 +48,10 @@ class TaskRepository extends BaseRepository
|
||||
'tasks.time_log as duration',
|
||||
'tasks.created_at',
|
||||
'tasks.created_at as date',
|
||||
'tasks.user_id'
|
||||
'tasks.user_id',
|
||||
'projects.name as project',
|
||||
'projects.public_id as project_public_id',
|
||||
'projects.user_id as project_user_id'
|
||||
);
|
||||
|
||||
if ($clientPublicId) {
|
||||
@ -84,7 +89,9 @@ class TaskRepository extends BaseRepository
|
||||
$query->where('clients.name', 'like', '%'.$filter.'%')
|
||||
->orWhere('contacts.first_name', 'like', '%'.$filter.'%')
|
||||
->orWhere('contacts.last_name', 'like', '%'.$filter.'%')
|
||||
->orWhere('tasks.description', 'like', '%'.$filter.'%');
|
||||
->orWhere('tasks.description', 'like', '%'.$filter.'%')
|
||||
->orWhere('contacts.email', 'like', '%'.$filter.'%')
|
||||
->orWhere('projects.name', 'like', '%'.$filter.'%');
|
||||
});
|
||||
}
|
||||
|
||||
@ -105,9 +112,13 @@ class TaskRepository extends BaseRepository
|
||||
return $task;
|
||||
}
|
||||
|
||||
if (isset($data['client']) && $data['client']) {
|
||||
$task->client_id = Client::getPrivateId($data['client']);
|
||||
if (isset($data['client'])) {
|
||||
$task->client_id = $data['client'] ? Client::getPrivateId($data['client']) : null;
|
||||
}
|
||||
if (isset($data['project_id'])) {
|
||||
$task->project_id = $data['project_id'] ? Project::getPrivateId($data['project_id']) : null;
|
||||
}
|
||||
|
||||
if (isset($data['description'])) {
|
||||
$task->description = trim($data['description']);
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ class AccountTransformer extends EntityTransformer
|
||||
'users',
|
||||
'products',
|
||||
'tax_rates',
|
||||
'expense_categories'
|
||||
'expense_categories',
|
||||
'projects',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -36,6 +37,16 @@ class AccountTransformer extends EntityTransformer
|
||||
return $this->includeCollection($account->expense_categories, $transformer, 'expense_categories');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return \League\Fractal\Resource\Collection
|
||||
*/
|
||||
public function includeProjects(Account $account)
|
||||
{
|
||||
$transformer = new ProjectTransformer($account, $this->serializer);
|
||||
return $this->includeCollection($account->projects, $transformer, 'projects');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return \League\Fractal\Resource\Collection
|
||||
|
@ -23,7 +23,7 @@ class ExpenseTransformer extends EntityTransformer
|
||||
'transaction_id' => $expense->transaction_id,
|
||||
'bank_id' => $expense->bank_id,
|
||||
'expense_currency_id' => (int) $expense->expense_currency_id,
|
||||
'expense_category_id' => (int) $expense->expense_category_id,
|
||||
'expense_category_id' => $expense->expense_category ? (int) $expense->expense_category->public_id : null,
|
||||
'amount' => (float) $expense->amount,
|
||||
'expense_date' => $expense->expense_date,
|
||||
'exchange_rate' => (float) $expense->exchange_rate,
|
||||
|
18
app/Ninja/Transformers/ProjectTransformer.php
Normal file
18
app/Ninja/Transformers/ProjectTransformer.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php namespace App\Ninja\Transformers;
|
||||
|
||||
use App\Models\Project;
|
||||
|
||||
class ProjectTransformer extends EntityTransformer
|
||||
{
|
||||
public function transform(Project $project)
|
||||
{
|
||||
return array_merge($this->getDefaults($project), [
|
||||
'id' => (int) $project->public_id,
|
||||
'name' => $project->name,
|
||||
'client_id' => $project->client ? (int) $project->client->public_id : null,
|
||||
'updated_at' => $this->getTimestamp($project->updated_at),
|
||||
'archived_at' => $this->getTimestamp($project->deleted_at),
|
||||
'is_deleted' => (bool) $project->is_deleted,
|
||||
]);
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@ class TaskTransformer extends EntityTransformer
|
||||
'archived_at' => (int) $this->getTimestamp($task->deleted_at),
|
||||
'invoice_id' => $task->invoice ? (int) $task->invoice->public_id : false,
|
||||
'client_id' => $task->client ? (int) $task->client->public_id : false,
|
||||
'project_id' => $task->project ? (int) $task->project->public_id : false,
|
||||
'is_deleted' => (bool) $task->is_deleted,
|
||||
'time_log' => $task->time_log,
|
||||
'is_running' => (bool) $task->is_running,
|
||||
|
10
app/Policies/ProjectPolicy.php
Normal file
10
app/Policies/ProjectPolicy.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
class ProjectPolicy extends EntityPolicy
|
||||
{
|
||||
|
||||
}
|
@ -28,6 +28,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
\App\Models\AccountToken::class => \App\Policies\TokenPolicy::class,
|
||||
\App\Models\BankAccount::class => \App\Policies\BankAccountPolicy::class,
|
||||
\App\Models\PaymentTerm::class => \App\Policies\PaymentTermPolicy::class,
|
||||
\App\Models\Project::class => \App\Policies\ProjectPolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
71
app/Services/ProjectService.php
Normal file
71
app/Services/ProjectService.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php namespace App\Services;
|
||||
|
||||
use Utils;
|
||||
use Auth;
|
||||
use App\Models\Client;
|
||||
use App\Ninja\Repositories\ProjectRepository;
|
||||
use App\Ninja\Datatables\ProjectDatatable;
|
||||
|
||||
/**
|
||||
* Class ProjectService
|
||||
*/
|
||||
class ProjectService extends BaseService
|
||||
{
|
||||
/**
|
||||
* @var ProjectRepository
|
||||
*/
|
||||
protected $projectRepo;
|
||||
|
||||
/**
|
||||
* @var DatatableService
|
||||
*/
|
||||
protected $datatableService;
|
||||
|
||||
/**
|
||||
* CreditService constructor.
|
||||
*
|
||||
* @param ProjectRepository $creditRepo
|
||||
* @param DatatableService $datatableService
|
||||
*/
|
||||
public function __construct(ProjectRepository $projectRepo, DatatableService $datatableService)
|
||||
{
|
||||
$this->projectRepo = $projectRepo;
|
||||
$this->datatableService = $datatableService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CreditRepository
|
||||
*/
|
||||
protected function getRepo()
|
||||
{
|
||||
return $this->projectRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if (isset($data['client_id']) && $data['client_id']) {
|
||||
$data['client_id'] = Client::getPrivateId($data['client_id']);
|
||||
}
|
||||
|
||||
return $this->projectRepo->save($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $clientPublicId
|
||||
* @param $search
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getDatatable($search, $userId)
|
||||
{
|
||||
// we don't support bulk edit and hide the client on the individual client page
|
||||
$datatable = new ProjectDatatable();
|
||||
|
||||
$query = $this->projectRepo->find($search, $userId);
|
||||
|
||||
return $this->datatableService->createDatatable($datatable, $query);
|
||||
}
|
||||
}
|
106
database/migrations/2016_11_28_092904_add_task_projects.php
Normal file
106
database/migrations/2016_11_28_092904_add_task_projects.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddTaskProjects extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('projects', function($table)
|
||||
{
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('user_id');
|
||||
$table->unsignedInteger('account_id')->index();
|
||||
$table->unsignedInteger('client_id')->index()->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->string('name')->nullable();
|
||||
$table->boolean('is_deleted')->default(false);
|
||||
|
||||
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
|
||||
|
||||
$table->unsignedInteger('public_id')->index();
|
||||
$table->unique( array('account_id','public_id') );
|
||||
});
|
||||
|
||||
Schema::table('tasks', function ($table)
|
||||
{
|
||||
$table->unsignedInteger('project_id')->nullable()->index();
|
||||
});
|
||||
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
|
||||
Schema::table('tasks', function ($table)
|
||||
{
|
||||
$table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');
|
||||
});
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
|
||||
|
||||
// is_deleted to standardize tables
|
||||
Schema::table('expense_categories', function ($table)
|
||||
{
|
||||
$table->boolean('is_deleted')->default(false);
|
||||
});
|
||||
|
||||
Schema::table('products', function ($table)
|
||||
{
|
||||
$table->boolean('is_deleted')->default(false);
|
||||
});
|
||||
|
||||
// add 'delete cascase' to resolve error when deleting an account
|
||||
Schema::table('account_gateway_tokens', function($table)
|
||||
{
|
||||
$table->dropForeign('account_gateway_tokens_default_payment_method_id_foreign');
|
||||
});
|
||||
|
||||
Schema::table('account_gateway_tokens', function($table)
|
||||
{
|
||||
$table->foreign('default_payment_method_id')->references('id')->on('payment_methods')->onDelete('cascade');
|
||||
});
|
||||
|
||||
Schema::table('invoices', function ($table)
|
||||
{
|
||||
$table->boolean('is_public')->default(false);
|
||||
});
|
||||
DB::table('invoices')->update(['is_public' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('tasks', function ($table)
|
||||
{
|
||||
$table->dropForeign('tasks_project_id_foreign');
|
||||
$table->dropColumn('project_id');
|
||||
});
|
||||
|
||||
Schema::dropIfExists('projects');
|
||||
|
||||
Schema::table('expense_categories', function ($table)
|
||||
{
|
||||
$table->dropColumn('is_deleted');
|
||||
});
|
||||
|
||||
Schema::table('products', function ($table)
|
||||
{
|
||||
$table->dropColumn('is_deleted');
|
||||
});
|
||||
|
||||
Schema::table('invoices', function ($table)
|
||||
{
|
||||
$table->dropColumn('is_public');
|
||||
});
|
||||
}
|
||||
}
|
@ -2228,6 +2228,28 @@ $LANG = array(
|
||||
'payment_status_name' => 'Status',
|
||||
'client_created_at' => 'Date Created',
|
||||
'postmark_error' => 'There was a problem sending the email through Postmark: :link',
|
||||
'project' => 'Project',
|
||||
'projects' => 'Projects',
|
||||
'new_project' => 'New Project',
|
||||
'edit_project' => 'Edit Project',
|
||||
'archive_project' => 'Archive Project',
|
||||
'list_projects' => 'List Projects',
|
||||
'updated_project' => 'Successfully updated project',
|
||||
'created_project' => 'Successfully created project',
|
||||
'archived_project' => 'Successfully archived project',
|
||||
'archived_projects' => 'Successfully archived :count projects',
|
||||
'restore_project' => 'Restore project',
|
||||
'restored_project' => 'Successfully restored project',
|
||||
'delete_project' => 'Delete project',
|
||||
'deleted_project' => 'Successfully deleted project',
|
||||
'deleted_projects' => 'Successfully deleted :count projects',
|
||||
'delete_expense_category' => 'Delete category',
|
||||
'deleted_expense_category' => 'Successfully deleted category',
|
||||
'delete_product' => 'Delete product',
|
||||
'deleted_product' => 'Successfully deleted product',
|
||||
'deleted_products' => 'Successfully deleted :count products',
|
||||
'restored_product' => 'Successfully restored product',
|
||||
|
||||
|
||||
);
|
||||
|
||||
|
@ -506,7 +506,9 @@
|
||||
'settings',
|
||||
//'self-update'
|
||||
] as $option)
|
||||
@if (in_array($option, ['dashboard', 'settings']) || Auth::user()->can('view', substr($option, 0, -1)))
|
||||
@if (in_array($option, ['dashboard', 'settings'])
|
||||
|| Auth::user()->can('view', substr($option, 0, -1))
|
||||
|| Auth::user()->can('create', substr($option, 0, -1)))
|
||||
<li class="{{ Request::is("{$option}*") ? 'active' : '' }}">
|
||||
@if ($option == 'settings')
|
||||
<a type="button" class="btn btn-default btn-sm pull-right"
|
||||
|
@ -17,14 +17,10 @@
|
||||
@endif
|
||||
@endcan
|
||||
|
||||
@if (in_array($entityType, [ENTITY_EXPENSE_CATEGORY, ENTITY_PRODUCT]))
|
||||
{!! Button::normal(trans('texts.archive'))->asLinkTo('javascript:submitForm_'.$entityType.'("archive")')->appendIcon(Icon::create('trash')) !!}
|
||||
@else
|
||||
{!! DropdownButton::normal(trans('texts.archive'))->withContents([
|
||||
['label' => trans('texts.archive_'.$entityType), 'url' => 'javascript:submitForm_'.$entityType.'("archive")'],
|
||||
['label' => trans('texts.delete_'.$entityType), 'url' => 'javascript:submitForm_'.$entityType.'("delete")'],
|
||||
])->withAttributes(['class'=>'archive'])->split() !!}
|
||||
@endif
|
||||
|
||||
|
||||
<span id="statusWrapper_{{ $entityType }}" style="display:none">
|
||||
@ -53,15 +49,15 @@
|
||||
<input id="tableFilter_{{ $entityType }}" type="text" style="width:140px;margin-right:17px;background-color: white !important"
|
||||
class="form-control pull-left" placeholder="{{ trans('texts.filter') }}" value="{{ Input::get('filter') }}"/>
|
||||
|
||||
@if (empty($clientId))
|
||||
@if ($entityType == ENTITY_EXPENSE)
|
||||
{!! Button::normal(trans('texts.categories'))->asLinkTo(URL::to('/expense_categories'))->appendIcon(Icon::create('list')) !!}
|
||||
@elseif ($entityType == ENTITY_TASK)
|
||||
{!! Button::normal(trans('texts.projects'))->asLinkTo(URL::to('/projects'))->appendIcon(Icon::create('list')) !!}
|
||||
@endif
|
||||
|
||||
@if (empty($vendorId) && Auth::user()->can('create', $entityType))
|
||||
@if (empty($clientId) && empty($vendorId) && Auth::user()->can('create', $entityType))
|
||||
{!! Button::primary(trans("texts.new_{$entityType}"))->asLinkTo(url(Utils::pluralizeEntityType($entityType) . '/create'))->appendIcon(Icon::create('plus-sign')) !!}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
@ -73,7 +69,7 @@
|
||||
->setCustomValues('entityType', Utils::pluralizeEntityType($entityType))
|
||||
->setCustomValues('clientId', isset($clientId) && $clientId)
|
||||
->setOptions('sPaginationType', 'bootstrap')
|
||||
->setOptions('aaSorting', [[$datatable->sortCol, 'desc']])
|
||||
->setOptions('aaSorting', [[isset($clientId) ? ($datatable->sortCol-1) : $datatable->sortCol, 'desc']])
|
||||
->render('datatable') !!}
|
||||
|
||||
@if ($entityType == ENTITY_PAYMENT)
|
||||
|
@ -57,7 +57,7 @@
|
||||
}
|
||||
|
||||
if (account && ! decorator) {
|
||||
decorator = account.show_currency_code ? 'code' : 'symbol';
|
||||
decorator = parseInt(account.show_currency_code) ? 'code' : 'symbol';
|
||||
}
|
||||
|
||||
return formatMoney(value, currencyId, countryId, decorator)
|
||||
|
73
resources/views/projects/edit.blade.php
Normal file
73
resources/views/projects/edit.blade.php
Normal file
@ -0,0 +1,73 @@
|
||||
@extends('header')
|
||||
|
||||
@section('content')
|
||||
|
||||
{!! Former::open($url)
|
||||
->addClass('col-md-10 col-md-offset-1 warn-on-exit')
|
||||
->method($method)
|
||||
->rules([
|
||||
'name' => 'required',
|
||||
]) !!}
|
||||
|
||||
@if ($project)
|
||||
{!! Former::populate($project) !!}
|
||||
@endif
|
||||
|
||||
<span style="display:none">
|
||||
{!! Former::text('public_id') !!}
|
||||
</span>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.project') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
{!! Former::text('name') !!}
|
||||
|
||||
{!! Former::select('client_id')
|
||||
->addOption('', '')
|
||||
->label(trans('texts.client')) !!}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<center class="buttons">
|
||||
{!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(url('/expense_categories'))->appendIcon(Icon::create('remove-circle')) !!}
|
||||
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
</center>
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script>
|
||||
|
||||
var clients = {!! $clients !!};
|
||||
|
||||
$(function() {
|
||||
$('#name').focus();
|
||||
|
||||
var $clientSelect = $('select#client_id');
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
var clientName = getClientDisplayName(client);
|
||||
if (!clientName) {
|
||||
continue;
|
||||
}
|
||||
$clientSelect.append(new Option(clientName, client.public_id));
|
||||
}
|
||||
@if ($clientPublicId)
|
||||
$clientSelect.val({{ $clientPublicId }});
|
||||
@endif
|
||||
$clientSelect.combobox();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@stop
|
@ -48,6 +48,8 @@
|
||||
<div class="panel-body">
|
||||
|
||||
{!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!}
|
||||
{!! Former::select('project_id')->addOption('', '')->addGroupClass('project-select')
|
||||
->label(trans('texts.project')) !!}
|
||||
{!! Former::textarea('description')->rows(3) !!}
|
||||
|
||||
@if ($task)
|
||||
@ -208,6 +210,8 @@
|
||||
}
|
||||
|
||||
var clients = {!! $clients !!};
|
||||
var projects = {!! $projects !!};
|
||||
|
||||
var timeLabels = {};
|
||||
@foreach (['hour', 'minute', 'second'] as $period)
|
||||
timeLabels['{{ $period }}'] = '{{ trans("texts.{$period}") }}';
|
||||
@ -425,22 +429,6 @@
|
||||
ko.applyBindings(model);
|
||||
|
||||
$(function() {
|
||||
var $clientSelect = $('select#client');
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
var clientName = getClientDisplayName(client);
|
||||
if (!clientName) {
|
||||
continue;
|
||||
}
|
||||
$clientSelect.append(new Option(clientName, client.public_id));
|
||||
}
|
||||
|
||||
if ({{ $clientPublicId ? 'true' : 'false' }}) {
|
||||
$clientSelect.val({{ $clientPublicId }});
|
||||
}
|
||||
|
||||
$clientSelect.combobox();
|
||||
|
||||
@if (!$task && !$clientPublicId)
|
||||
$('.client-select input.form-control').focus();
|
||||
@else
|
||||
@ -494,6 +482,98 @@
|
||||
model.showTimeOverlaps();
|
||||
showTimeDetails();
|
||||
@endif
|
||||
|
||||
// setup clients and project comboboxes
|
||||
var clientId = {{ $clientPublicId }};
|
||||
var projectId = {{ $projectPublicId }};
|
||||
|
||||
var clientMap = {};
|
||||
var projectMap = {};
|
||||
var projectsForClientMap = {};
|
||||
var projectsForAllClients = [];
|
||||
var $clientSelect = $('select#client');
|
||||
|
||||
for (var i=0; i<projects.length; i++) {
|
||||
var project = projects[i];
|
||||
projectMap[project.public_id] = project;
|
||||
|
||||
var client = project.client;
|
||||
if (!client) {
|
||||
projectsForAllClients.push(project);
|
||||
} else {
|
||||
if (!projectsForClientMap.hasOwnProperty(client.public_id)) {
|
||||
projectsForClientMap[client.public_id] = [];
|
||||
}
|
||||
projectsForClientMap[client.public_id].push(project);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
clientMap[client.public_id] = client;
|
||||
}
|
||||
|
||||
$clientSelect.append(new Option('', ''));
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
var clientName = getClientDisplayName(client);
|
||||
if (!clientName) {
|
||||
continue;
|
||||
}
|
||||
$clientSelect.append(new Option(clientName, client.public_id));
|
||||
}
|
||||
|
||||
if (clientId) {
|
||||
$clientSelect.val(clientId);
|
||||
}
|
||||
|
||||
$clientSelect.combobox();
|
||||
$clientSelect.on('change', function(e) {
|
||||
var clientId = $('input[name=client]').val();
|
||||
var projectId = $('input[name=project_id]').val();
|
||||
var project = projectMap[projectId];
|
||||
if (project && ((project.client && project.client.public_id == clientId) || !project.client)) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
setComboboxValue($('.project-select'), '', '');
|
||||
$projectCombobox = $('select#project_id');
|
||||
$projectCombobox.find('option').remove().end().combobox('refresh');
|
||||
$projectCombobox.append(new Option('', ''));
|
||||
var list = clientId ? (projectsForClientMap.hasOwnProperty(clientId) ? projectsForClientMap[clientId] : []).concat(projectsForAllClients) : projects;
|
||||
for (var i=0; i<list.length; i++) {
|
||||
var project = list[i];
|
||||
$projectCombobox.append(new Option(project.name, project.public_id));
|
||||
}
|
||||
$('select#project_id').combobox('refresh');
|
||||
});
|
||||
|
||||
var $projectSelect = $('select#project_id').on('change', function(e) {
|
||||
$clientCombobox = $('select#client');
|
||||
var projectId = $('input[name=project_id]').val();
|
||||
if (projectId) {
|
||||
var project = projectMap[projectId];
|
||||
if (project.client) {
|
||||
var client = clientMap[project.client.public_id];
|
||||
if (client) {
|
||||
project.client = client;
|
||||
setComboboxValue($('.client-select'), client.public_id, getClientDisplayName(client));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$clientSelect.trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
$projectSelect.combobox();
|
||||
|
||||
if (projectId) {
|
||||
var project = projectMap[projectId];
|
||||
setComboboxValue($('.project-select'), project.public_id, project.name);
|
||||
$projectSelect.trigger('change');
|
||||
} else {
|
||||
$clientSelect.trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user