From 2525f68a39dbcf8f0f40f19bf1a2a709cfaab3cf Mon Sep 17 00:00:00 2001 From: steenrabol Date: Tue, 19 Jan 2016 20:35:15 +0100 Subject: [PATCH] Expense / Vendor module bug fix --- app/Http/Controllers/ExpenseApiController.php | 244 ++++++++++++++++++ app/Http/routes.php | 12 +- app/Models/Expense.php | 2 +- app/Ninja/Repositories/ExpenseRepository.php | 54 ++-- app/Services/ExpenseService.php | 86 +++++- resources/views/vendors/show.blade.php | 4 +- 6 files changed, 365 insertions(+), 37 deletions(-) create mode 100644 app/Http/Controllers/ExpenseApiController.php diff --git a/app/Http/Controllers/ExpenseApiController.php b/app/Http/Controllers/ExpenseApiController.php new file mode 100644 index 000000000000..484538455586 --- /dev/null +++ b/app/Http/Controllers/ExpenseApiController.php @@ -0,0 +1,244 @@ +expenseRepo = $expenseRepo; + $this->expenseService = $expenseService; + } + + /** + * Display a listing of the resource. + * + * @return Response + */ + public function index() + { + return View::make('list', array( + 'entityType' => ENTITY_EXPENSE, + 'title' => trans('texts.expenses'), + 'sortCol' => '1', + 'columns' => Utils::trans([ + 'checkbox', + 'vendor', + 'expense_amount', + 'expense_date', + 'public_notes', + 'is_invoiced', + 'should_be_invoiced', + '' + ]), + )); + } + + public function getDatatable($expensePublicId = null) + { + return $this->expenseService->getDatatable($expensePublicId, Input::get('sSearch')); + } + + public function getDatatableVendor($vendorPublicId = null) + { + return $this->expenseService->getDatatableVendor($vendorPublicId); + } + + public function create($vendorPublicId = 0) + { + if($vendorPublicId != 0) { + $vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail(); + } else { + $vendor = null; + } + $data = array( + 'vendorPublicId' => Input::old('vendor') ? Input::old('vendor') : $vendorPublicId, + 'expense' => null, + 'method' => 'POST', + 'url' => 'expenses', + 'title' => trans('texts.new_expense'), + 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendor' => $vendor, + 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), + 'clientPublicId' => null, + ); + + $data = array_merge($data, self::getViewModel()); + + return View::make('expenses.edit', $data); + } + + public function edit($publicId) + { + $expense = Expense::scope($publicId)->firstOrFail(); + $expense->expense_date = Utils::fromSqlDate($expense->expense_date); + + $data = array( + 'vendor' => null, + 'expense' => $expense, + 'method' => 'PUT', + 'url' => 'expenses/'.$publicId, + 'title' => 'Edit Expense', + 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendorPublicId' => $expense->vendor_id, + 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), + 'clientPublicId' => $expense->invoice_client_id, + ); + + $data = array_merge($data, self::getViewModel()); + + if (Auth::user()->account->isNinjaAccount()) { + if ($account = Account::whereId($client->public_id)->first()) { + $data['proPlanPaid'] = $account['pro_plan_paid']; + } + } + + return View::make('expenses.edit', $data); + } + + /** + * Update the specified resource in storage. + * + * @param int $id + * @return Response + */ + public function update(UpdateExpenseRequest $request) + { + $client = $this->expenseRepo->save($request->input()); + + Session::flash('message', trans('texts.updated_expense')); + + return redirect()->to('expenses'); + } + + public function store(CreateExpenseRequest $request) + { + $expense = $this->expenseRepo->save($request->input()); + + Session::flash('message', trans('texts.created_expense')); + + return redirect()->to('expenses'); + } + + public function bulk() + { + $action = Input::get('action'); + $ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids'); + + switch($action) + { + case 'invoice': + $expenses = Expense::scope($ids)->get(); + $clientPublicId = null; + $data = []; + + // Validate that either all expenses do not have a client or if there is a client, it is the same client + foreach ($expenses as $expense) + { + if ($expense->client_id) { + if (!$clientPublicId) { + $clientPublicId = $expense->client_id; + } else if ($clientPublicId != $expense->client_id) { + Session::flash('error', trans('texts.expense_error_multiple_clients')); + return Redirect::to('expenses'); + } + } + + if ($expense->invoice_id) { + Session::flash('error', trans('texts.expense_error_invoiced')); + return Redirect::to('expenses'); + } + + if ($expense->should_be_invoiced == 0) { + Session::flash('error', trans('texts.expense_error_should_not_be_invoiced')); + return Redirect::to('expenses'); + } + + $account = Auth::user()->account; + $data[] = [ + 'publicId' => $expense->public_id, + 'description' => $expense->public_notes, + 'qty' => 1, + 'cost' => $expense->amount, + ]; + } + + return Redirect::to("invoices/create/{$clientPublicId}")->with('expenses', $data); + break; + + default: + $count = $this->expenseService->bulk($ids, $action); + } + + if ($count > 0) { + $message = Utils::pluralize($action.'d_expense', $count); + Session::flash('message', $message); + } + + return Redirect::to('expenses'); + } + + private static function getViewModel() + { + return [ + 'data' => Input::old('data'), + 'account' => Auth::user()->account, + 'sizes' => Cache::get('sizes'), + 'paymentTerms' => Cache::get('paymentTerms'), + 'industries' => Cache::get('industries'), + 'currencies' => Cache::get('currencies'), + 'languages' => Cache::get('languages'), + 'countries' => Cache::get('countries'), + 'customLabel1' => Auth::user()->account->custom_vendor_label1, + 'customLabel2' => Auth::user()->account->custom_vendor_label2, + ]; + } + + public function show($publicId) + { + $expense = Expense::withTrashed()->scope($publicId)->firstOrFail(); + + if($expense) { + Utils::trackViewed($expense->getDisplayName(), 'expense'); + } + + $actionLinks = [ + ['label' => trans('texts.new_expense'), 'url' => '/expenses/create/'] + ]; + + $data = array( + 'actionLinks' => $actionLinks, + 'showBreadcrumbs' => false, + 'expense' => $expense, + 'credit' =>0, + 'vendor' => $expense->vendor, + 'title' => trans('texts.view_expense',['expense' => $expense->public_id]), + ); + + return View::make('expenses.show', $data); + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index 61a180c2746e..451ad1323537 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -198,10 +198,10 @@ Route::group(['middleware' => 'auth'], function() { // Expense Route::resource('expenses', 'ExpenseController'); Route::get('expenses/create/{vendor_id?}', 'ExpenseController@create'); - Route::post('expenses/bulk', 'ExpenseController@bulk'); - Route::get('api/expense/', array('as'=>'api.expenses', 'uses'=>'ExpenseController@getDatatable')); + Route::get('api/expense', array('as'=>'api.expenses', 'uses'=>'ExpenseApiController@getDatatable')); + Route::get('api/expenseVendor/{id}', array('as'=>'api.expense', 'uses'=>'ExpenseApiController@getDatatableVendor')); Route::get('api/expenseactivities/{expense_id?}', array('as'=>'api.expenseactivities', 'uses'=>'ExpenseActivityController@getDatatable')); - + Route::post('expenses/bulk', 'ExpenseController@bulk'); }); // Route groups for API @@ -223,8 +223,12 @@ Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function() Route::post('hooks', 'IntegrationController@subscribe'); Route::post('email_invoice', 'InvoiceApiController@emailInvoice'); Route::get('user_accounts','AccountApiController@getUserAccounts'); + // Vendor Route::resource('vendors', 'VendorApiController'); + + //Expense + Route::resource('expenses', 'ExpenseApiController'); }); // Redirects for legacy links @@ -580,7 +584,7 @@ if (!defined('CONTACT_EMAIL')) { 'dateFormats' => 'App\Models\DateFormat', 'datetimeFormats' => 'App\Models\DatetimeFormat', 'languages' => 'App\Models\Language', - //'paymentTerms' => 'App\Models\PaymentTerm', + 'paymentTerms' => 'App\Models\PaymentTerm', 'paymentTypes' => 'App\Models\PaymentType', 'countries' => 'App\Models\Country', 'invoiceDesigns' => 'App\Models\InvoiceDesign', diff --git a/app/Models/Expense.php b/app/Models/Expense.php index daf652d4e415..d97bc5099121 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -12,7 +12,7 @@ class Expense extends EntityModel use SoftDeletes; use PresentableTrait; - protected $dates = ['deleted_at']; + protected $dates = ['deleted_at','expense_date']; protected $presenter = 'App\Ninja\Presenters\ExpensePresenter'; protected $fillable = [ diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 80ce74ee3694..d1c6cb86622f 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -23,7 +23,23 @@ class ExpenseRepository extends BaseRepository ->where('is_deleted', '=', false) ->get(); } - + + public function findVendor($vendorPublicId) + { + $accountid = \Auth::user()->account_id; + $query = DB::table('expenses') + ->join('accounts', 'accounts.id', '=', 'expenses.account_id') + ->where('expenses.account_id', '=', $accountid) + ->where('expenses.vendor_id','=',$vendorPublicId) + ->select('expenses.id', + 'expenses.expense_date', + 'expenses.amount', + 'expenses.public_notes', + 'expenses.public_id', + 'expenses.deleted_at','expenses.is_invoiced','expenses.should_be_invoiced','expenses.created_at'); + return $query; + } + public function find($filter = null) { $accountid = \Auth::user()->account_id; @@ -50,19 +66,11 @@ class ExpenseRepository extends BaseRepository 'vendors.public_id as vendor_public_id'); $showTrashed = \Session::get('show_trash:expense'); - - //var_dump($showTrashed); - + if (!$showTrashed) { $query->where('expenses.deleted_at', '=', null); } - - /* - if (!\Session::get('show_trash:expense')) { - $query->where('expenses.deleted_at', '=', null); - } - */ - + if ($filter) { $query->where(function ($query) use ($filter) { $query->where('expenses.public_notes', 'like', '%'.$filter.'%'); @@ -84,36 +92,36 @@ class ExpenseRepository extends BaseRepository // First auto fill $expense->fill($input); - + // We can have an expense without a vendor if(isset($input['vendor'])) { - $expense->vendor_id = $input['vendor']; + $expense->vendor_id = $input['vendor']; } - + $expense->expense_date = Utils::toSqlDate($input['expense_date']); $expense->amount = Utils::parseFloat($input['amount']); - + if(isset($input['amount_cur'])) $expense->amount_cur = Utils::parseFloat($input['amount_cur']); - + $expense->private_notes = trim($input['private_notes']); $expense->public_notes = trim($input['public_notes']); - + if(isset($input['exchange_rate'])) $expense->exchange_rate = Utils::parseFloat($input['exchange_rate']); else $expense->exchange_rate = 100; - + if($expense->exchange_rate == 0) $expense->exchange_rate = 100; - + // set the currency if(isset($input['currency_id'])) $expense->currency_id = $input['currency_id']; - + if($expense->currency_id == 0) $expense->currency_id = Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY); - + // Calculate the amount cur $expense->amount_cur = ($expense->amount / 100) * $expense->exchange_rate; @@ -125,7 +133,7 @@ class ExpenseRepository extends BaseRepository return $expense; } - + public function bulk($ids, $action) { $expenses = Expense::withTrashed()->scope($ids)->get(); @@ -148,5 +156,5 @@ class ExpenseRepository extends BaseRepository return count($tasks); } - + } diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index 07a02445d655..558864798953 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -36,6 +36,16 @@ class ExpenseService extends BaseService return $this->createDatatable(ENTITY_EXPENSE, $query); } + public function getDatatableVendor($vendorPublicId) + { + $query = $this->expenseRepo->findVendor($vendorPublicId); + return $this->datatableService->createDatatable(ENTITY_EXPENSE, + $query, + $this->getDatatableColumnsVendor(ENTITY_EXPENSE,false), + $this->getDatatableActionsVendor(ENTITY_EXPENSE), + false); + } + protected function getDatatableColumns($entityType, $hideClient) { return [ @@ -80,12 +90,49 @@ class ExpenseService extends BaseService return $model->should_be_invoiced ? trans('texts.yes') : trans('texts.no'); } ], - /*[ - 'public_id', - function($model) { - return link_to("expenses/{$model->public_id}", trans('texts.view', ['expense' => $model->public_id])); + ]; + } + + protected function getDatatableColumnsVendor($entityType, $hideClient) + { + return [ + /* + [ + 'expenses.id', + function ($model) { + return Utils::timestampToDateTimeString(strtotime($model->created_at)); } - ]*/ + ],*/ + [ + 'expense_date', + function ($model) { + return $model->expense_date; + } + ], + [ + 'amount', + function ($model) { + return Utils::formatMoney($model->amount, false, false); + } + ], + [ + 'public_notes', + function ($model) { + return $model->public_notes != null ? $model->public_notes : ''; + } + ], + [ + 'is_invoiced', + function ($model) { + return $model->is_invoiced ? trans('texts.yes') : trans('texts.no'); + } + ], + [ + 'should_be_invoiced', + function ($model) { + return $model->should_be_invoiced ? trans('texts.yes') : trans('texts.no'); + } + ], ]; } @@ -110,7 +157,32 @@ class ExpenseService extends BaseService return URL::to("expenses/{$model->public_id}/edit") ; } ], - + ]; } -} \ No newline at end of file + protected function getDatatableActionsVendor($entityType) + { + return [ + [ + trans('texts.invoice_expense'), + function ($model) { + return URL::to("expense/invoice/{$model->public_id}") . '?client=1'; + } + ], + [ + trans('texts.view'), + function ($model) { + return URL::to("expenses/{$model->public_id}") ; + } + ], + [ + trans('texts.edit'), + function ($model) { + return URL::to("expenses/{$model->public_id}/edit") ; + } + ], + + ]; + } + +} diff --git a/resources/views/vendors/show.blade.php b/resources/views/vendors/show.blade.php index bf7040c6d8e0..1b6b402629ed 100644 --- a/resources/views/vendors/show.blade.php +++ b/resources/views/vendors/show.blade.php @@ -165,8 +165,8 @@ ->addColumn( trans('texts.expense_date'), trans('texts.amount'), - trans('texts.private_notes')) - ->setUrl(url('api/expense/' . $vendor->public_id)) + trans('texts.public_notes')) + ->setUrl(url('api/expenseVendor/' . $vendor->public_id)) ->setCustomValues('entityType', 'expenses') ->setOptions('sPaginationType', 'bootstrap') ->setOptions('bFilter', false)