From d3ee8ed81382375de574270c460e715dfa6bacd0 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Tue, 5 Jan 2016 12:47:03 +0100 Subject: [PATCH 01/20] Expense module --- app/Providers/AppServiceProvider.php | 9 ++- ...2016_01_04_175228_create_vendors_table.php | 64 +++++++++++++++++++ resources/views/header.blade.php | 1 + 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2016_01_04_175228_create_vendors_table.php diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index bf67c12498cc..84a93e177b40 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -53,7 +53,14 @@ class AppServiceProvider extends ServiceProvider { $str .= '
  • '.trans("texts.credits").'
  • '.trans("texts.new_credit").'
  • '; - } + } else if($type == 'expense'){ + $str .= '
  • +
  • '.trans("texts.expenses").'
  • +
  • '.trans("texts.new_expense").'
  • +
  • +
  • '.trans("texts.vendors").'
  • +
  • '.trans("texts.new_vendor").'
  • '; + } $str .= ' '; diff --git a/database/migrations/2016_01_04_175228_create_vendors_table.php b/database/migrations/2016_01_04_175228_create_vendors_table.php new file mode 100644 index 000000000000..67d4f6c088fa --- /dev/null +++ b/database/migrations/2016_01_04_175228_create_vendors_table.php @@ -0,0 +1,64 @@ +increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->integer('account_id', false, true); + $table->integer('currency_id',false, true)->nullable(); + $table->string('name')->nullable(); + $table->string('address1'); + $table->string('address2'); + $table->string('city'); + $table->string('state'); + $table->string('postal_code'); + $table->integer('country_id',false, true); + $table->string('work_phone'); + $table->text('private_notes'); + $table->decimal('balance',13,2); + $table->decimal('paid_to_date',13,2); + + //$table->dateTime('last_login'); + + $table->string('website'); + $table->integer('industry_id',false, true); + $table->integer('size_id'); + $table->tinyInteger('is_deleted')->default(0); + $table->integer('payment_terms')->nullable(); + $table->integer('public_id',false, true); + $table->string('custom_value1')->nullable(); + $table->string('custom_value2')->nullable(); + $table->string('vat_number')->nullable(); + $table->string('id_number')->nullable(); + $table->integer('language_id', false, true)->nullable(); + }); + + // add relations + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('vendors'); + } + +} diff --git a/resources/views/header.blade.php b/resources/views/header.blade.php index cc20ef0de34d..60cad066a5ec 100644 --- a/resources/views/header.blade.php +++ b/resources/views/header.blade.php @@ -374,6 +374,7 @@ {!! HTML::menu_link('task') !!} {!! HTML::menu_link('invoice') !!} {!! HTML::menu_link('payment') !!} + {!! HTML::menu_link('expense') !!} diff --git a/resources/views/vendor.blade.php b/resources/views/vendor.blade.php new file mode 100644 index 000000000000..924a4fb30dc1 --- /dev/null +++ b/resources/views/vendor.blade.php @@ -0,0 +1,99 @@ + +
    +
    + + {!! Former::legend('Organization') !!} + {!! Former::text('name') !!} + {!! Former::text('id_number') !!} + {!! Former::text('vat_number') !!} + {!! Former::text('work_phone')->label('Phone') !!} + {!! Former::textarea('notes') !!} + + + {!! Former::legend('Address') !!} + {!! Former::text('address1')->label('Street') !!} + {!! Former::text('address2')->label('Apt/Floor') !!} + {!! Former::text('city') !!} + {!! Former::text('state') !!} + {!! Former::text('postal_code') !!} + {!! Former::select('country_id')->addOption('','')->label('Country') + ->fromQuery($countries, 'name', 'id') !!} + + +
    +
    + + {!! Former::legend('VendorContacts') !!} +
    + {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!} + +
    +
    + + {!! link_to('#', 'Remove contact', array('data-bind'=>'click: $parent.removeContact')) !!} + + + {!! link_to('#', 'Add contact', array('onclick'=>'return addContact()')) !!} + +
    +
    + +
    + +
    +
    + + +{!! Former::hidden('data')->data_bind("value: ko.toJSON(model)") !!} + + + diff --git a/resources/views/vendors/edit.blade.php b/resources/views/vendors/edit.blade.php new file mode 100644 index 000000000000..aaf25c1c0ee5 --- /dev/null +++ b/resources/views/vendors/edit.blade.php @@ -0,0 +1,236 @@ +@extends('header') + + +@section('onReady') + $('input#name').focus(); +@stop + +@section('content') + +@if ($errors->first('vendor_contacts')) +
    {{ trans($errors->first('vendor_contacts')) }}
    +@endif + +
    + + {!! Former::open($url) + ->autocomplete('off') + ->rules( + ['email' => 'email'] + )->addClass('col-md-12 warn-on-exit') + ->method($method) !!} + + @include('partials.autocomplete_fix') + + @if ($vendor) + {!! Former::populate($vendor) !!} + {!! Former::hidden('public_id') !!} + @endif + +
    +
    + + +
    +
    +

    {!! trans('texts.organization') !!}

    +
    +
    + + {!! Former::text('name')->data_bind("attr { placeholder: placeholderName }") !!} + {!! Former::text('id_number') !!} + {!! Former::text('vat_number') !!} + {!! Former::text('website') !!} + {!! Former::text('work_phone') !!} + + @if (Auth::user()->isPro()) + @if ($customLabel1) + {!! Former::text('custom_value1')->label($customLabel1) !!} + @endif + @if ($customLabel2) + {!! Former::text('custom_value2')->label($customLabel2) !!} + @endif + @endif +
    +
    + +
    +
    +

    {!! trans('texts.address') !!}

    +
    +
    + + {!! Former::text('address1') !!} + {!! Former::text('address2') !!} + {!! Former::text('city') !!} + {!! Former::text('state') !!} + {!! Former::text('postal_code') !!} + {!! Former::select('country_id')->addOption('','') + ->fromQuery($countries, 'name', 'id') !!} + +
    +
    +
    +
    + + +
    +
    +

    {!! trans('texts.contacts') !!}

    +
    +
    + +
    + {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown', + attr: {name: 'vendor_contacts[' + \$index() + '][public_id]'}") !!} + {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', + attr: {name: 'vendor_contacts[' + \$index() + '][first_name]'}") !!} + {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown', + attr: {name: 'vendor_contacts[' + \$index() + '][last_name]'}") !!} + {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', + attr: {name: 'vendor_contacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} + {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', + attr: {name: 'vendor_contacts[' + \$index() + '][phone]'}") !!} + +
    +
    + + {!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!} + + + {!! link_to('#', trans('texts.add_contact').' +', array('onclick'=>'return addContact()')) !!} + +
    +
    +
    +
    +
    + + +
    +
    +

    {!! trans('texts.additional_info') !!}

    +
    +
    + + {!! Former::select('currency_id')->addOption('','') + ->placeholder($account->currency ? $account->currency->name : '') + ->fromQuery($currencies, 'name', 'id') !!} + {!! Former::select('language_id')->addOption('','') + ->placeholder($account->language ? $account->language->name : '') + ->fromQuery($languages, 'name', 'id') !!} + {!! Former::select('payment_terms')->addOption('','') + ->fromQuery($paymentTerms, 'name', 'num_days') + ->help(trans('texts.payment_terms_help')) !!} + {!! Former::select('size_id')->addOption('','') + ->fromQuery($sizes, 'name', 'id') !!} + {!! Former::select('industry_id')->addOption('','') + ->fromQuery($industries, 'name', 'id') !!} + {!! Former::textarea('private_notes') !!} + + + @if (isset($proPlanPaid)) + {!! Former::populateField('pro_plan_paid', $proPlanPaid) !!} + {!! Former::text('pro_plan_paid') + ->data_date_format('yyyy-mm-dd') + ->addGroupClass('pro_plan_paid_date') + ->append('') !!} + + @endif + +
    +
    + +
    +
    + + + {!! Former::hidden('data')->data_bind("value: ko.toJSON(model)") !!} + + + +
    + {!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/vendors/' . ($vendor ? $vendor->public_id : '')))->appendIcon(Icon::create('remove-circle')) !!} + {!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!} +
    + + {!! Former::close() !!} +
    +@stop diff --git a/resources/views/vendors/show.blade.php b/resources/views/vendors/show.blade.php new file mode 100644 index 000000000000..7fea8b976ec2 --- /dev/null +++ b/resources/views/vendors/show.blade.php @@ -0,0 +1,304 @@ +@extends('header') + +@section('head') + @parent + + @if ($vendor->hasAddress()) + + + + @endif +@stop + + +@section('content') + +
    + {!! Former::open('vendors/bulk')->addClass('mainForm') !!} +
    + {!! Former::text('action') !!} + {!! Former::text('public_id')->value($vendor->public_id) !!} +
    + + @if ($gatewayLink) + {!! Button::normal(trans('texts.view_in_stripe'))->asLinkTo($gatewayLink)->withAttributes(['target' => '_blank']) !!} + @endif + + @if ($vendor->trashed()) + {!! Button::primary(trans('texts.restore_vendor'))->withAttributes(['onclick' => 'onRestoreClick()']) !!} + @else + {!! DropdownButton::normal(trans('texts.edit_vendor')) + ->withAttributes(['class'=>'normalDropDown']) + ->withContents([ + ['label' => trans('texts.archive_vendor'), 'url' => "javascript:onArchiveClick()"], + ['label' => trans('texts.delete_vendor'), 'url' => "javascript:onDeleteClick()"], + ] + )->split() !!} + + {!! DropdownButton::primary(trans('texts.new_expense')) + ->withAttributes(['class'=>'primaryDropDown']) + ->withContents($actionLinks)->split() !!} + @endif + {!! Former::close() !!} + +
    + + +

    {{ $vendor->getDisplayName() }}

    + {{-- + @if ($vendor->last_login > 0) +

    + {{ trans('texts.last_logged_in') }} {{ Utils::timestampToDateTimeString(strtotime($vendor->last_login)) }} +

    + @endif + --}} +
    +
    +
    + +
    +

    {{ trans('texts.details') }}

    + @if ($vendor->id_number) +

    {{ trans('texts.id_number').': '.$vendor->id_number }}

    + @endif + @if ($vendor->vat_number) +

    {{ trans('texts.vat_number').': '.$vendor->vat_number }}

    + @endif + + @if ($vendor->address1) + {{ $vendor->address1 }}
    + @endif + @if ($vendor->address2) + {{ $vendor->address2 }}
    + @endif + @if ($vendor->getCityState()) + {{ $vendor->getCityState() }}
    + @endif + @if ($vendor->country) + {{ $vendor->country->name }}
    + @endif + + @if ($vendor->account->custom_vendor_label1 && $vendor->custom_value1) + {{ $vendor->account->custom_vendor_label1 . ': ' . $vendor->custom_value1 }}
    + @endif + @if ($vendor->account->custom_vendor_label2 && $vendor->custom_value2) + {{ $vendor->account->custom_vendor_label2 . ': ' . $vendor->custom_value2 }}
    + @endif + + @if ($vendor->work_phone) + {{ $vendor->work_phone }} + @endif + + @if ($vendor->private_notes) +

    {{ $vendor->private_notes }}

    + @endif + + @if ($vendor->vendor_industry) + {{ $vendor->vendor_industry->name }}
    + @endif + @if ($vendor->vendor_size) + {{ $vendor->vendor_size->name }}
    + @endif + + @if ($vendor->website) +

    {!! Utils::formatWebsite($vendor->website) !!}

    + @endif + + @if ($vendor->language) +

    {{ $vendor->language->name }}

    + @endif + +

    {{ $vendor->payment_terms ? trans('texts.payment_terms') . ": " . trans('texts.payment_terms_net') . " " . $vendor->payment_terms : '' }}

    +
    + +
    +

    {{ trans('texts.contacts') }}

    + @foreach ($vendor->vendorcontacts as $contact) + @if ($contact->first_name || $contact->last_name) + {{ $contact->first_name.' '.$contact->last_name }}
    + @endif + @if ($contact->email) + {!! HTML::mailto($contact->email, $contact->email) !!}
    + @endif + @if ($contact->phone) + {{ $contact->phone }}
    + @endif + @endforeach +
    + +
    +

    {{ trans('texts.standing') }} + + + + + + + + + + @if ($credit > 0) + + + + + @endif +
    {{ trans('texts.paid_to_date') }}{{ Utils::formatMoney($vendor->paid_to_date, $vendor->getCurrencyId()) }}
    {{ trans('texts.balance') }}{{ Utils::formatMoney($vendor->balance, $vendor->getCurrencyId()) }}
    {{ trans('texts.credit') }}{{ Utils::formatMoney($credit, $vendor->getCurrencyId()) }}
    +

    +
    +
    +
    +
    + + @if ($vendor->hasAddress()) +
    +
    + @endif + + + +
    +
    + + {!! Datatable::table() + ->addColumn( + trans('texts.date'), + trans('texts.message'), + trans('texts.balance'), + trans('texts.adjustment')) + ->setUrl(url('api/vendoractivities/'. $vendor->public_id)) + ->setCustomValues('entityType', 'activity') + ->setOptions('sPaginationType', 'bootstrap') + ->setOptions('bFilter', false) + ->setOptions('aaSorting', [['0', 'desc']]) + ->render('datatable') !!} + +
    + +
    + + {!! Datatable::table() + ->addColumn( + trans('texts.credit_amount'), + trans('texts.credit_balance'), + trans('texts.credit_date'), + trans('texts.private_notes')) + ->setUrl(url('api/credits/' . $vendor->public_id)) + ->setCustomValues('entityType', 'credits') + ->setOptions('sPaginationType', 'bootstrap') + ->setOptions('bFilter', false) + ->setOptions('aaSorting', [['0', 'asc']]) + ->render('datatable') + !!} + +
    +
    + + + +@stop From 48ce3f64e649fc55127722140ff60e09d4340230 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Wed, 6 Jan 2016 20:52:09 +0100 Subject: [PATCH 04/20] expenses --- app/Events/ExpenseWasArchived.php | 23 +++ app/Events/ExpenseWasCreated.php | 22 +++ app/Events/ExpenseWasDeleted.php | 23 +++ app/Events/ExpenseWasRestored.php | 23 +++ app/Http/Controllers/ExpenseController.php | 131 ++++++++++++++++++ app/Http/Requests/CreateExpenseRequest.php | 29 ++++ app/Http/Requests/UpdateExpenseRequest.php | 28 ++++ app/Http/Requests/UpdatePaymentRequest.php | 8 +- app/Http/routes.php | 23 ++- app/Listeners/ExpenseListener.php | 16 +++ app/Listeners/SubscriptionListener.php | 7 + app/Models/Expense.php | 79 +++++++++++ app/Models/ExpenseAcitvity.php | 56 ++++++++ app/Ninja/Presenters/ExpensePresenter.php | 17 +++ app/Ninja/Repositories/ExpenseRepository.php | 94 +++++++++++++ .../Transformers/VendorContactTransformer.php | 24 ++++ app/Ninja/Transformers/VendorTransformer.php | 91 ++++++++++++ app/Providers/EventServiceProvider.php | 15 ++ app/Services/ExpenseService.php | 84 +++++++++++ ...016_01_06_155001_create_expenses_table.php | 56 ++++++++ public/js/built.js | 11 ++ resources/lang/en/texts.php | 12 ++ 22 files changed, 868 insertions(+), 4 deletions(-) create mode 100644 app/Events/ExpenseWasArchived.php create mode 100644 app/Events/ExpenseWasCreated.php create mode 100644 app/Events/ExpenseWasDeleted.php create mode 100644 app/Events/ExpenseWasRestored.php create mode 100644 app/Http/Controllers/ExpenseController.php create mode 100644 app/Http/Requests/CreateExpenseRequest.php create mode 100644 app/Http/Requests/UpdateExpenseRequest.php create mode 100644 app/Listeners/ExpenseListener.php create mode 100644 app/Models/Expense.php create mode 100644 app/Models/ExpenseAcitvity.php create mode 100644 app/Ninja/Presenters/ExpensePresenter.php create mode 100644 app/Ninja/Repositories/ExpenseRepository.php create mode 100644 app/Ninja/Transformers/VendorContactTransformer.php create mode 100644 app/Ninja/Transformers/VendorTransformer.php create mode 100644 app/Services/ExpenseService.php create mode 100644 database/migrations/2016_01_06_155001_create_expenses_table.php diff --git a/app/Events/ExpenseWasArchived.php b/app/Events/ExpenseWasArchived.php new file mode 100644 index 000000000000..7ff42b7b97e2 --- /dev/null +++ b/app/Events/ExpenseWasArchived.php @@ -0,0 +1,23 @@ +expense = $expense; + } + +} diff --git a/app/Events/ExpenseWasCreated.php b/app/Events/ExpenseWasCreated.php new file mode 100644 index 000000000000..efeb308d9bc9 --- /dev/null +++ b/app/Events/ExpenseWasCreated.php @@ -0,0 +1,22 @@ +expense = $expense; + } +} diff --git a/app/Events/ExpenseWasDeleted.php b/app/Events/ExpenseWasDeleted.php new file mode 100644 index 000000000000..a058cf1666e1 --- /dev/null +++ b/app/Events/ExpenseWasDeleted.php @@ -0,0 +1,23 @@ +expense = $expense; + } + +} diff --git a/app/Events/ExpenseWasRestored.php b/app/Events/ExpenseWasRestored.php new file mode 100644 index 000000000000..ca308e4ca0f8 --- /dev/null +++ b/app/Events/ExpenseWasRestored.php @@ -0,0 +1,23 @@ +expense = $expense; + } + +} diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php new file mode 100644 index 000000000000..c441a62dd0f6 --- /dev/null +++ b/app/Http/Controllers/ExpenseController.php @@ -0,0 +1,131 @@ +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' => '4', + 'columns' => Utils::trans([ + 'checkbox', + 'vendor', + 'expense_amount', + 'expense_balance', + 'expense_date', + 'private_notes', + '' + ]), + )); + } + + public function getDatatable($vendorPublicId = null) + { + return $this->expenseService->getDatatable($vendorPublicId, Input::get('sSearch')); + } + + public function create($vendorPublicId = 0) + { + $vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail(); + $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(), + ); + + $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(), ); + + return View::make('expense.edit', $data); + } + + 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'); + $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, + ]; + } + +} diff --git a/app/Http/Requests/CreateExpenseRequest.php b/app/Http/Requests/CreateExpenseRequest.php new file mode 100644 index 000000000000..0a0c71dd8208 --- /dev/null +++ b/app/Http/Requests/CreateExpenseRequest.php @@ -0,0 +1,29 @@ + 'required|positive', + ]; + } +} diff --git a/app/Http/Requests/UpdateExpenseRequest.php b/app/Http/Requests/UpdateExpenseRequest.php new file mode 100644 index 000000000000..83b192280849 --- /dev/null +++ b/app/Http/Requests/UpdateExpenseRequest.php @@ -0,0 +1,28 @@ + 'required|positive', + ]; + } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 3f7ed3a84969..f4bdc8865d9c 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -188,6 +188,21 @@ Route::group(['middleware' => 'auth'], function() { Route::get('api/vendor', array('as'=>'api.vendors', 'uses'=>'VendorController@getDatatable')); Route::get('api/vendoractivities/{vendor_id?}', array('as'=>'api.vendoractivities', 'uses'=>'VendorActivityController@getDatatable')); Route::post('vendors/bulk', 'VendorController@bulk'); + + // Expense + Route::get('expenses/{id}/edit', function() { + return View::make('header'); + }); + + Route::resource('expenses', 'ExpenseController'); + Route::get('expenses/create/{vendor_id?}', 'ExpenseController@create'); + Route::get('api/expenses/{vendor_id?}', array('as'=>'api.expenses', 'uses'=>'ExpenseController@getDatatable')); + + //Route::get('api/expenseactivities/{vendor_id?}', array('as'=>'api.expenseactivities', 'uses'=>'ExpenseActivityController@getDatatable')); + //Route::post('vendors/bulk', 'VendorController@bulk'); + + + Route::post('expenses/bulk', 'ExpenseController@bulk'); }); @@ -252,6 +267,7 @@ if (!defined('CONTACT_EMAIL')) { define('ENV_STAGING', 'staging'); define('RECENTLY_VIEWED', 'RECENTLY_VIEWED'); + define('ENTITY_CLIENT', 'client'); define('ENTITY_CONTACT', 'contact'); define('ENTITY_INVOICE', 'invoice'); @@ -346,6 +362,12 @@ if (!defined('CONTACT_EMAIL')) { define('ACTIVITY_TYPE_DELETE_VENDOR', 32); define('ACTIVITY_TYPE_RESTORE_VENDOR', 33); + // expenses + define('ACTIVITY_TYPE_CREATE_EXPENSE', 34); + define('ACTIVITY_TYPE_ARCHIVE_EXPENSE', 35); + define('ACTIVITY_TYPE_DELETE_EXPENSE', 36); + define('ACTIVITY_TYPE_RESTORE_EXPENSE', 37); + define('DEFAULT_INVOICE_NUMBER', '0001'); define('RECENTLY_VIEWED_LIMIT', 8); define('LOGGED_ERROR_LIMIT', 100); @@ -379,7 +401,6 @@ if (!defined('CONTACT_EMAIL')) { define('MAX_NUM_VENDORS_PRO', 20000); define('MAX_NUM_VENDORS_LEGACY', 500); - define('INVOICE_STATUS_DRAFT', 1); define('INVOICE_STATUS_SENT', 2); define('INVOICE_STATUS_VIEWED', 3); diff --git a/app/Listeners/ExpenseListener.php b/app/Listeners/ExpenseListener.php new file mode 100644 index 000000000000..8d02f00ff4db --- /dev/null +++ b/app/Listeners/ExpenseListener.php @@ -0,0 +1,16 @@ +expenseRepo = $expenseRepo; + } +} diff --git a/app/Listeners/SubscriptionListener.php b/app/Listeners/SubscriptionListener.php index 60643a25e6dd..fab5a2c57493 100644 --- a/app/Listeners/SubscriptionListener.php +++ b/app/Listeners/SubscriptionListener.php @@ -10,6 +10,7 @@ use App\Events\CreditWasCreated; use App\Events\PaymentWasCreated; use App\Events\VendorWasCreated; +use App\Events\ExpenseWasCreated; class SubscriptionListener { @@ -51,4 +52,10 @@ class SubscriptionListener { $this->checkSubscriptions(ACTIVITY_TYPE_CREATE_VENDOR, $event->vendor); } + + public function createdExpense(ExpenseWasCreated $event) + { + $this->checkSubscriptions(ACTIVITY_TYPE_CREATE_EXPENSE, $event->expense); + } + } diff --git a/app/Models/Expense.php b/app/Models/Expense.php new file mode 100644 index 000000000000..c1522fa1bda4 --- /dev/null +++ b/app/Models/Expense.php @@ -0,0 +1,79 @@ +belongsTo('App\Models\Account'); + } + + public function user() + { + return $this->belongsTo('App\Models\User'); + } + + public function vendor() + { + return $this->belongsTo('App\Models\Vendor')->withTrashed(); + } + + public function getName() + { + return ''; + } + + public function getEntityType() + { + return ENTITY_EXPENSE; + } + + public function apply($amount) + { + if ($amount > $this->balance) { + $applied = $this->balance; + $this->balance = 0; + } else { + $applied = $amount; + $this->balance = $this->balance - $amount; + } + + $this->save(); + + return $applied; + } +} + +Expense::creating(function ($expense) { + $expense->setNullValues(); +}); + +Expense::created(function ($expense) { + event(new ExpenseWasCreated($expense)); +}); + +Expense::updating(function ($expense) { + $expense->setNullValues(); +}); + +Expense::updated(function ($expense) { + event(new ExpenseWasUpdated($expense)); +}); + + + diff --git a/app/Models/ExpenseAcitvity.php b/app/Models/ExpenseAcitvity.php new file mode 100644 index 000000000000..5a4166141668 --- /dev/null +++ b/app/Models/ExpenseAcitvity.php @@ -0,0 +1,56 @@ +whereAccountId(Auth::user()->account_id); + } + + public function account() + { + return $this->belongsTo('App\Models\Account'); + } + + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + + public function vendor() + { + return $this->belongsTo('App\Models\Vendor')->withTrashed(); + } + + public function getMessage() + { + $activityTypeId = $this->activity_type_id; + $account = $this->account; + $vendor = $this->vendor; + $user = $this->user; + $contactId = $this->contact_id; + $isSystem = $this->is_system; + + if($vendor) { + $route = $vendor->getRoute(); + + $data = [ + 'vendor' => link_to($route, $vendor->getDisplayName()), + 'user' => $isSystem ? '' . trans('texts.system') . '' : $user->getDisplayName(), + 'vendorcontact' => $contactId ? $vendor->getDisplayName() : $user->getDisplayName(), + ]; + } else { + return trans("texts.invalid_activity"); + } + return trans("texts.activity_{$activityTypeId}", $data); + } +} diff --git a/app/Ninja/Presenters/ExpensePresenter.php b/app/Ninja/Presenters/ExpensePresenter.php new file mode 100644 index 000000000000..72da1cfef2f1 --- /dev/null +++ b/app/Ninja/Presenters/ExpensePresenter.php @@ -0,0 +1,17 @@ +entity->vendor ? $this->entity->vendor->getDisplayName() : ''; + } + + public function expense_date() + { + return Utils::fromSqlDate($this->entity->expense_date); + } +} \ No newline at end of file diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php new file mode 100644 index 000000000000..f585b740436b --- /dev/null +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -0,0 +1,94 @@ +join('accounts', 'accounts.id', '=', 'expenses.account_id') + ->join('vendors', 'vendors.id', '=', 'expenses.vendor_id') + ->join('vendor_contacts', 'vendor_contacts.vendor_id', '=', 'vendors.id') + ->where('vendors.account_id', '=', \Auth::user()->account_id) + ->where('vendors.deleted_at', '=', null) + ->where('vendor_contacts.deleted_at', '=', null) + ->where('vendor_contacts.is_primary', '=', true) + ->select( + DB::raw('COALESCE(vendors.currency_id, accounts.currency_id) currency_id'), + DB::raw('COALESCE(vendors.country_id, accounts.country_id) country_id'), + 'expenses.public_id', + 'vendors.name as vendor_name', + 'vendors.public_id as vendor_public_id', + 'expenses.amount', + 'expenses.balance', + 'expenses.expense_date', + 'vendor_contacts.first_name', + 'vendor_contacts.last_name', + 'vendor_contacts.email', + 'expenses.private_notes', + 'expenses.deleted_at', + 'expenses.is_deleted' + ); + + if ($vendorPublicId) { + $query->where('vendors.public_id', '=', $vendorPublicId); + } + + if (!\Session::get('show_trash:expense')) { + $query->where('expenses.deleted_at', '=', null); + } + + if ($filter) { + $query->where(function ($query) use ($filter) { + $query->where('vendors.name', 'like', '%'.$filter.'%'); + }); + } + + return $query; + } + + public function save($input) + { + $publicId = isset($input['public_id']) ? $input['public_id'] : false; + + if ($publicId) { + $expense = Expense::scope($publicId)->firstOrFail(); + } else { + $expense = Expense::createNew(); + } + + // First auto fille + $expense->fill($input); + + // We can have an expense without a vendor + if(isset($input['vendor'])) { + $expense->vendor_id = Vendor::getPrivateId($input['vendor']); + } + + $expense->expense_date = Utils::toSqlDate($input['expense_date']); + $expense->amount = Utils::parseFloat($input['amount']); + + if(isset($input['amountcur'])) + $expense->amountcur = Utils::parseFloat($input['amountcur']); + + $expense->balance = Utils::parseFloat($input['amount']); + $expense->private_notes = trim($input['private_notes']); + + if(isset($input['exchange_rate'])) + $expense->exchange_rate = Utils::parseFloat($input['exchange_rate']); + + $expense->save(); + + return $expense; + } +} diff --git a/app/Ninja/Transformers/VendorContactTransformer.php b/app/Ninja/Transformers/VendorContactTransformer.php new file mode 100644 index 000000000000..9a34ac740289 --- /dev/null +++ b/app/Ninja/Transformers/VendorContactTransformer.php @@ -0,0 +1,24 @@ + (int) $contact->public_id, + 'first_name' => $contact->first_name, + 'last_name' => $contact->last_name, + 'email' => $contact->email, + 'updated_at' => $this->getTimestamp($contact->updated_at), + 'archived_at' => $this->getTimestamp($contact->deleted_at), + 'is_primary' => (bool) $contact->is_primary, + 'phone' => $contact->phone, + 'last_login' => $contact->last_login, + 'account_key' => $this->account->account_key, + ]; + } +} \ No newline at end of file diff --git a/app/Ninja/Transformers/VendorTransformer.php b/app/Ninja/Transformers/VendorTransformer.php new file mode 100644 index 000000000000..3ef965a15b97 --- /dev/null +++ b/app/Ninja/Transformers/VendorTransformer.php @@ -0,0 +1,91 @@ +account, $this->serializer); + return $this->includeCollection($vendor->contacts, $transformer, ENTITY_CONTACT); + } + + public function includeInvoices(Vendor $vendor) + { + $transformer = new InvoiceTransformer($this->account, $this->serializer); + return $this->includeCollection($vendor->invoices, $transformer, ENTITY_INVOICE); + } + + public function transform(Vendor $vendor) + { + return [ + 'id' => (int) $vendor->public_id, + 'name' => $vendor->name, + 'balance' => (float) $vendor->balance, + 'paid_to_date' => (float) $vendor->paid_to_date, + 'user_id' => (int) $vendor->user->public_id + 1, + 'account_key' => $this->account->account_key, + 'updated_at' => $this->getTimestamp($vendor->updated_at), + 'archived_at' => $this->getTimestamp($vendor->deleted_at), + 'address1' => $vendor->address1, + 'address2' => $vendor->address2, + 'city' => $vendor->city, + 'state' => $vendor->state, + 'postal_code' => $vendor->postal_code, + 'country_id' => (int) $vendor->country_id, + 'work_phone' => $vendor->work_phone, + 'private_notes' => $vendor->private_notes, + 'last_login' => $vendor->last_login, + 'website' => $vendor->website, + 'industry_id' => (int) $vendor->industry_id, + 'size_id' => (int) $vendor->size_id, + 'is_deleted' => (bool) $vendor->is_deleted, + 'payment_terms' => (int) $vendor->payment_terms, + 'vat_number' => $vendor->vat_number, + 'id_number' => $vendor->id_number, + 'language_id' => (int) $vendor->language_id, + 'currency_id' => (int) $vendor->currency_id + ]; + } +} \ No newline at end of file diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index def84e1fdddd..dbdfe9b7395b 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -151,6 +151,21 @@ class EventServiceProvider extends ServiceProvider { 'App\Events\VendorWasRestored' => [ 'App\Listeners\VendorActivityListener@restoredVendor', ], + + // Expense events + 'App\Events\ExpenseWasCreated' => [ + 'App\Listeners\ExpenseActivityListener@createdExpense', + 'App\Listeners\SubscriptionListener@createdExpense', + ], + 'App\Events\ExpenseWasArchived' => [ + 'App\Listeners\ExpenseActivityListener@archivedExpense', + ], + 'App\Events\ExpenseWasDeleted' => [ + 'App\Listeners\ExpenseActivityListener@deletedExpense', + ], + 'App\Events\ExpenseWasRestored' => [ + 'App\Listeners\ExpenseActivityListener@restoredExpense', + ], ]; diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php new file mode 100644 index 000000000000..fb6333622315 --- /dev/null +++ b/app/Services/ExpenseService.php @@ -0,0 +1,84 @@ +expenseRepo = $expenseRepo; + $this->datatableService = $datatableService; + } + + protected function getRepo() + { + return $this->expenseRepo; + } + + public function save($data) + { + return $this->expenseRepo->save($data); + } + + public function getDatatable($vendorPublicId, $search) + { + $query = $this->expenseRepo->find($vendorPublicId, $search); + + return $this->createDatatable(ENTITY_CREDIT, $query, !$vendorPublicId); + } + + protected function getDatatableColumns($entityType, $hideClient) + { + return [ + [ + 'vendor_name', + function ($model) { + return $model->vendor_public_id ? link_to("vendors/{$model->vendor_public_id}", Utils::getVendorDisplayName($model)) : ''; + }, + ! $hideClient + ], + [ + 'amount', + function ($model) { + return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id) . ''; + } + ], + [ + 'balance', + function ($model) { + return Utils::formatMoney($model->balance, $model->currency_id, $model->country_id); + } + ], + [ + 'expense_date', + function ($model) { + return Utils::fromSqlDate($model->expense_date); + } + ], + [ + 'private_notes', + function ($model) { + return $model->private_notes; + } + ] + ]; + } + + protected function getDatatableActions($entityType) + { + return [ + [ + trans('texts.apply_expense'), + function ($model) { + return URL::to("espense/create/{$model->vendor_public_id}") . '?paymentTypeId=1'; + } + ] + ]; + } +} \ No newline at end of file diff --git a/database/migrations/2016_01_06_155001_create_expenses_table.php b/database/migrations/2016_01_06_155001_create_expenses_table.php new file mode 100644 index 000000000000..12dc669203f4 --- /dev/null +++ b/database/migrations/2016_01_06_155001_create_expenses_table.php @@ -0,0 +1,56 @@ +increments('id'); + $table->timestamps(); + + $table->unsignedInteger('account_id')->index(); + $table->unsignedInteger('vendor_id')->nullable(); + $table->unsignedInteger('user_id'); + + $table->softDeletes(); + + $table->boolean('is_deleted')->default(false); + $table->decimal('amount', 13, 2); + $table->decimal('amountcur', 13, 2); + $table->decimal('exchange_rate', 13, 2); + $table->decimal('balance', 13, 2); + $table->date('expense_date')->nullable(); + $table->string('expense_number')->nullable(); + $table->text('private_notes'); + $table->integer('currency_id',false, true)->nullable(); + $table->boolean('is_invoiced')->default(false); + $table->boolean('should_be_invoiced')->default(true); + + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');; + + $table->unsignedInteger('public_id')->index(); + $table->unique( array('account_id','public_id') ); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('expenses'); + } +} diff --git a/public/js/built.js b/public/js/built.js index 6c00fbe7cbbe..b3186d0b7aef 100644 --- a/public/js/built.js +++ b/public/js/built.js @@ -30298,6 +30298,17 @@ function getClientDisplayName(client) return ''; } +function getVendorDisplayName(vendor) +{ + var contact = vendor.contacts ? vendor.vendorcontacts[0] : false; + if (vendor.name) { + return vendor.name; + } else if (contact) { + return getContactDisplayName(contact); + } + return ''; +} + function populateInvoiceComboboxes(clientId, invoiceId) { var clientMap = {}; var invoiceMap = {}; diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index dbed40c5fa3c..9687bf330869 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -893,6 +893,13 @@ return array( 'activity_28' => ':user restored :credit credit', 'activity_29' => ':contact approved quote :quote', 'activity_30' => ':user created :vendor', + 'activity_31' => ':user created :vendor', + 'activity_32' => ':user created :vendor', + 'activity_33' => ':user created :vendor', + 'activity_34' => ':user created :vendor', + 'activity_35' => ':user created :vendor', + 'activity_36' => ':user created :vendor', + 'activity_37' => ':user created :vendor', 'payment' => 'Payment', 'system' => 'System', @@ -1018,4 +1025,9 @@ return array( 'archive_vendor' => 'Archive vendor', 'delete_vendor' => 'Delete vendor', 'view_vendor' => 'View vendor', + + // Expenses + 'expense_amount' => 'Expense amount', + 'expense_balance' => 'Expense balance', + 'expense_date' => 'Expense date', ); From 45ba7ffc3b826dd852de8e11a01465dbaa6cd402 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Thu, 7 Jan 2016 09:11:18 +0100 Subject: [PATCH 05/20] test --- app/Models/ExpenseAcitvity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/ExpenseAcitvity.php b/app/Models/ExpenseAcitvity.php index 5a4166141668..714dbf3f6664 100644 --- a/app/Models/ExpenseAcitvity.php +++ b/app/Models/ExpenseAcitvity.php @@ -7,7 +7,7 @@ use Session; use Request; use Carbon; -class ExpenseAcitvity extends Eloquent { +class ExpenseActivity extends Eloquent { public $timestamps = true; From a52364a0396d6740cd52a659afdbbbd171fc3846 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Thu, 7 Jan 2016 12:04:01 +0100 Subject: [PATCH 06/20] expense --- app/Http/Controllers/ExpenseController.php | 3 +- app/Http/routes.php | 14 +--- app/Models/Expense.php | 6 -- app/Models/ExpenseActivity.php | 64 +++++++++++++++ app/Ninja/Presenters/VendorPresenter.php | 12 +++ .../ExpenseActivityRepository.php | 80 +++++++++++++++++++ app/Ninja/Repositories/ExpenseRepository.php | 42 ++++++++-- app/Services/ExpenseService.php | 22 ++--- ...016_01_06_155001_create_expenses_table.php | 1 + ...191912_create_expense_activities_table.php | 51 ++++++++++++ resources/lang/en/texts.php | 4 +- 11 files changed, 262 insertions(+), 37 deletions(-) create mode 100644 app/Models/ExpenseActivity.php create mode 100644 app/Ninja/Presenters/VendorPresenter.php create mode 100644 app/Ninja/Repositories/ExpenseActivityRepository.php create mode 100644 database/migrations/2016_01_06_191912_create_expense_activities_table.php diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index c441a62dd0f6..8ecb5184ccd4 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -46,6 +46,7 @@ class ExpenseController extends BaseController 'expense_balance', 'expense_date', 'private_notes', + 'public_notes', '' ]), )); @@ -92,7 +93,7 @@ class ExpenseController extends BaseController public function store(CreateExpenseRequest $request) { $expense = $this->expenseRepo->save($request->input()); - + Session::flash('message', trans('texts.created_expense')); return redirect()->to('expenses'); diff --git a/app/Http/routes.php b/app/Http/routes.php index f4bdc8865d9c..c94f16641abe 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -190,20 +190,12 @@ Route::group(['middleware' => 'auth'], function() { Route::post('vendors/bulk', 'VendorController@bulk'); // Expense - Route::get('expenses/{id}/edit', function() { - return View::make('header'); - }); - Route::resource('expenses', 'ExpenseController'); Route::get('expenses/create/{vendor_id?}', 'ExpenseController@create'); - Route::get('api/expenses/{vendor_id?}', array('as'=>'api.expenses', 'uses'=>'ExpenseController@getDatatable')); - - //Route::get('api/expenseactivities/{vendor_id?}', array('as'=>'api.expenseactivities', 'uses'=>'ExpenseActivityController@getDatatable')); - //Route::post('vendors/bulk', 'VendorController@bulk'); - - Route::post('expenses/bulk', 'ExpenseController@bulk'); - + Route::get('api/expense/', array('as'=>'api.expenses', 'uses'=>'ExpenseController@getDatatable')); + Route::get('api/expenseactivities/{vendor_id?}', array('as'=>'api.expenseactivities', 'uses'=>'ExpenseActivityController@getDatatable')); + }); // Route groups for API diff --git a/app/Models/Expense.php b/app/Models/Expense.php index c1522fa1bda4..6376ac016f99 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -1,8 +1,5 @@ belongsTo('App\Models\Account'); diff --git a/app/Models/ExpenseActivity.php b/app/Models/ExpenseActivity.php new file mode 100644 index 000000000000..14f93e06c2f8 --- /dev/null +++ b/app/Models/ExpenseActivity.php @@ -0,0 +1,64 @@ +whereAccountId(Auth::user()->account_id); + } + + public function account() + { + return $this->belongsTo('App\Models\Account'); + } + + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + + public function vendor() + { + return $this->belongsTo('App\Models\Vendor')->withTrashed(); + } + + public function expense() + { + return $this->belongsTo('App\Models\Expense')->withTrashed(); + } + + public function getMessage() + { + $activityTypeId = $this->activity_type_id; + $account = $this->account; + $vendor = $this->vendor; + $user = $this->user; + $contactId = $this->contact_id; + $isSystem = $this->is_system; + $expense = $this->expense; + + if($expense) { + $route = link_to($expense->getRoute(), $vendor->getDisplayName()); + } else { + $route ='no expense id'; + } + + + + $data = [ + 'expense' => $route, + 'user' => $isSystem ? '' . trans('texts.system') . '' : $user->getDisplayName(), + ]; + + return trans("texts.activity_{$activityTypeId}", $data); + } +} diff --git a/app/Ninja/Presenters/VendorPresenter.php b/app/Ninja/Presenters/VendorPresenter.php new file mode 100644 index 000000000000..ab2c9f56349e --- /dev/null +++ b/app/Ninja/Presenters/VendorPresenter.php @@ -0,0 +1,12 @@ +entity->country ? $this->entity->country->name : ''; + } +} \ No newline at end of file diff --git a/app/Ninja/Repositories/ExpenseActivityRepository.php b/app/Ninja/Repositories/ExpenseActivityRepository.php new file mode 100644 index 000000000000..aa9dc7e9823e --- /dev/null +++ b/app/Ninja/Repositories/ExpenseActivityRepository.php @@ -0,0 +1,80 @@ +vendor_id = $entity->vendor_id; + $activity->contact_id = $entity->contact_id; + $activity->activity_type_id = $activityTypeId; + $activity->message = $activity->getMessage(); + $activity->expense_id = $entity->id; + $activity->save(); + + return $activity; + } + + private function getBlank($entity) + { + $activity = new ExpenseActivity(); + + if (Auth::check() && Auth::user()->account_id == $entity->account_id) { + $activity->user_id = Auth::user()->id; + $activity->account_id = Auth::user()->account_id; + } else { + $activity->user_id = $entity->user_id; + $activity->account_id = $entity->account_id; + } + + $activity->token_id = session('token_id'); + $activity->ip = Request::getClientIp(); + + return $activity; + } + + + public function findByVendorId($vendorId) + { + return DB::table('expense_activities') + ->join('accounts', 'accounts.id', '=', 'vendor_activities.account_id') + ->join('users', 'users.id', '=', 'vendor_activities.user_id') + ->join('vendors', 'vendors.id', '=', 'vendor_activities.vendor_id') + ->leftJoin('vendor_contacts', 'vendor_contacts.vendor_id', '=', 'vendors.id') + ->where('vendors.id', '=', $vendorId) + ->where('vendor_contacts.is_primary', '=', 1) + ->whereNull('vendor_contacts.deleted_at') + ->select( + DB::raw('COALESCE(vendors.currency_id, accounts.currency_id) currency_id'), + DB::raw('COALESCE(vendors.country_id, accounts.country_id) country_id'), + 'vendor_activities.id', + 'vendor_activities.created_at', + 'vendor_activities.contact_id', + 'vendor_activities.activity_type_id', + 'vendor_activities.is_system', + 'vendor_activities.balance', + 'vendor_activities.adjustment', + 'users.first_name as user_first_name', + 'users.last_name as user_last_name', + 'users.email as user_email', + 'vendors.name as vendor_name', + 'vendors.public_id as vendor_public_id', + 'vendor_contacts.id as contact', + 'vendor_contacts.first_name as first_name', + 'vendor_contacts.last_name as last_name', + 'vendor_contacts.email as email' + + ); + } +} \ No newline at end of file diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index f585b740436b..2cfaa600c062 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -13,8 +13,18 @@ class ExpenseRepository extends BaseRepository return 'App\Models\Expense'; } - public function find($vendorPublicId = null, $filter = null) + public function all() { + return Expense::scope() + ->with('user') + ->withTrashed() + ->where('is_deleted', '=', false) + ->get(); + } + + public function find($filter = null) + { + /* $query = DB::table('expenses') ->join('accounts', 'accounts.id', '=', 'expenses.account_id') ->join('vendors', 'vendors.id', '=', 'expenses.vendor_id') @@ -39,21 +49,37 @@ class ExpenseRepository extends BaseRepository 'expenses.deleted_at', 'expenses.is_deleted' ); - - if ($vendorPublicId) { - $query->where('vendors.public_id', '=', $vendorPublicId); - } - + */ + $accountid = \Auth::user()->account_id; + $query = DB::table('expenses') + ->join('accounts', 'accounts.id', '=', 'expenses.account_id') + //->join('vendors', 'vendors.id', '=', 'expenses.vendor_id') + ->where('expenses.account_id', '=', $accountid) + ->where('expenses.deleted_at', '=', null) + ->select( + //DB::raw('COALESCE(vendors.currency_id, accounts.currency_id) currency_id'), + //DB::raw('COALESCE(vendors.country_id, accounts.country_id) country_id'), + 'expenses.public_id', + //'vendors.name as vendor_name', + //'vendors.public_id as vendor_public_id', + 'expenses.amount', + 'expenses.balance', + 'expenses.expense_date', + 'expenses.public_notes', + 'expenses.deleted_at', + 'expenses.is_deleted' + ); + if (!\Session::get('show_trash:expense')) { $query->where('expenses.deleted_at', '=', null); } - +/* if ($filter) { $query->where(function ($query) use ($filter) { $query->where('vendors.name', 'like', '%'.$filter.'%'); }); } - +*/ return $query; } diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index fb6333622315..92ffc85306ce 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -5,6 +5,7 @@ use URL; use App\Services\BaseService; use App\Ninja\Repositories\ExpenseRepository; + class ExpenseService extends BaseService { protected $expenseRepo; @@ -26,33 +27,33 @@ class ExpenseService extends BaseService return $this->expenseRepo->save($data); } - public function getDatatable($vendorPublicId, $search) + public function getDatatable($search) { - $query = $this->expenseRepo->find($vendorPublicId, $search); + $query = $this->expenseRepo->find($search); - return $this->createDatatable(ENTITY_CREDIT, $query, !$vendorPublicId); + return $this->createDatatable(ENTITY_EXPENSE, $query); } protected function getDatatableColumns($entityType, $hideClient) { return [ - [ + /*[ 'vendor_name', function ($model) { return $model->vendor_public_id ? link_to("vendors/{$model->vendor_public_id}", Utils::getVendorDisplayName($model)) : ''; }, ! $hideClient - ], + ],*/ [ 'amount', function ($model) { - return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id) . ''; + return Utils::formatMoney($model->amount, false, false) . ''; } ], [ 'balance', function ($model) { - return Utils::formatMoney($model->balance, $model->currency_id, $model->country_id); + return Utils::formatMoney($model->balance, false, false); } ], [ @@ -62,14 +63,14 @@ class ExpenseService extends BaseService } ], [ - 'private_notes', + 'private_public', function ($model) { - return $model->private_notes; + return $model->public_notes; } ] ]; } - +/* protected function getDatatableActions($entityType) { return [ @@ -81,4 +82,5 @@ class ExpenseService extends BaseService ] ]; } + */ } \ No newline at end of file diff --git a/database/migrations/2016_01_06_155001_create_expenses_table.php b/database/migrations/2016_01_06_155001_create_expenses_table.php index 12dc669203f4..56e74fbc7963 100644 --- a/database/migrations/2016_01_06_155001_create_expenses_table.php +++ b/database/migrations/2016_01_06_155001_create_expenses_table.php @@ -32,6 +32,7 @@ class CreateExpensesTable extends Migration $table->date('expense_date')->nullable(); $table->string('expense_number')->nullable(); $table->text('private_notes'); + $table->text('public_notes'); $table->integer('currency_id',false, true)->nullable(); $table->boolean('is_invoiced')->default(false); $table->boolean('should_be_invoiced')->default(true); diff --git a/database/migrations/2016_01_06_191912_create_expense_activities_table.php b/database/migrations/2016_01_06_191912_create_expense_activities_table.php new file mode 100644 index 000000000000..a7106568cebe --- /dev/null +++ b/database/migrations/2016_01_06_191912_create_expense_activities_table.php @@ -0,0 +1,51 @@ +increments('id'); + $table->timestamps(); + + $table->unsignedInteger('account_id'); + $table->unsignedInteger('vendor_id'); + $table->unsignedInteger('user_id'); + $table->unsignedInteger('contact_id')->nullable(); + $table->unsignedInteger('expense_id'); + $table->unsignedInteger('invitation_id')->nullable(); + $table->text('message')->nullable(); + $table->text('json_backup')->nullable(); + $table->integer('activity_type_id'); + $table->decimal('adjustment', 13, 2)->nullable(); + $table->decimal('balance', 13, 2)->nullable(); + $table->unsignedInteger('token_id')->nullable(); + $table->string('ip')->nullable(); + $table->boolean('is_system')->default(0); + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('vendor_id')->references('id')->on('vendors')->onDelete('cascade'); + $table->foreign('expense_id')->references('id')->on('expenses')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('expense_activities'); + } + +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 9687bf330869..e7993b59ac58 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -896,7 +896,7 @@ return array( 'activity_31' => ':user created :vendor', 'activity_32' => ':user created :vendor', 'activity_33' => ':user created :vendor', - 'activity_34' => ':user created :vendor', + 'activity_34' => ':user created :expense', 'activity_35' => ':user created :vendor', 'activity_36' => ':user created :vendor', 'activity_37' => ':user created :vendor', @@ -1030,4 +1030,6 @@ return array( 'expense_amount' => 'Expense amount', 'expense_balance' => 'Expense balance', 'expense_date' => 'Expense date', + 'expense_exchange_rate_100' => 'The amount for 100 in company currency', + 'expense_should_be_invoiced' => 'Should this expense be invoiced?', ); From 4a0a9a3c99ced4e0f329fc13b5bc82f0b0c8fb88 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Thu, 7 Jan 2016 12:08:20 +0100 Subject: [PATCH 07/20] expense --- app/Models/ExpenseAcitvity.php | 56 ---------------------------------- 1 file changed, 56 deletions(-) delete mode 100644 app/Models/ExpenseAcitvity.php diff --git a/app/Models/ExpenseAcitvity.php b/app/Models/ExpenseAcitvity.php deleted file mode 100644 index 714dbf3f6664..000000000000 --- a/app/Models/ExpenseAcitvity.php +++ /dev/null @@ -1,56 +0,0 @@ -whereAccountId(Auth::user()->account_id); - } - - public function account() - { - return $this->belongsTo('App\Models\Account'); - } - - public function user() - { - return $this->belongsTo('App\Models\User')->withTrashed(); - } - - public function vendor() - { - return $this->belongsTo('App\Models\Vendor')->withTrashed(); - } - - public function getMessage() - { - $activityTypeId = $this->activity_type_id; - $account = $this->account; - $vendor = $this->vendor; - $user = $this->user; - $contactId = $this->contact_id; - $isSystem = $this->is_system; - - if($vendor) { - $route = $vendor->getRoute(); - - $data = [ - 'vendor' => link_to($route, $vendor->getDisplayName()), - 'user' => $isSystem ? '' . trans('texts.system') . '' : $user->getDisplayName(), - 'vendorcontact' => $contactId ? $vendor->getDisplayName() : $user->getDisplayName(), - ]; - } else { - return trans("texts.invalid_activity"); - } - return trans("texts.activity_{$activityTypeId}", $data); - } -} From 17af1e2b9c3c51e9a5adf7e12bd038df4e58b6b7 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Thu, 7 Jan 2016 16:14:11 +0100 Subject: [PATCH 08/20] Expenses --- app/Events/ExpenseWasArchived.php | 1 + app/Events/ExpenseWasCreated.php | 2 +- app/Events/ExpenseWasDeleted.php | 2 +- app/Events/ExpenseWasRestored.php | 2 +- app/Http/Controllers/ExpenseController.php | 1 + app/Http/Requests/CreateExpenseRequest.php | 1 + app/Http/Requests/UpdateExpenseRequest.php | 4 +- app/Listeners/ExpenseListener.php | 1 + app/Models/Expense.php | 1 + app/Models/ExpenseActivity.php | 2 +- app/Ninja/Presenters/ExpensePresenter.php | 1 + .../ExpenseActivityRepository.php | 1 + app/Ninja/Repositories/ExpenseRepository.php | 1 + app/Services/ExpenseService.php | 1 + ...016_01_06_155001_create_expenses_table.php | 1 + ...191912_create_expense_activities_table.php | 1 + invoiceninja.komodoproject | 43 +++++++++++++++++++ 17 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 invoiceninja.komodoproject diff --git a/app/Events/ExpenseWasArchived.php b/app/Events/ExpenseWasArchived.php index 7ff42b7b97e2..fe27998d29c4 100644 --- a/app/Events/ExpenseWasArchived.php +++ b/app/Events/ExpenseWasArchived.php @@ -6,6 +6,7 @@ use Illuminate\Queue\SerializesModels; class ExpenseWasArchived extends Event { + // Expenses use SerializesModels; public $expense; diff --git a/app/Events/ExpenseWasCreated.php b/app/Events/ExpenseWasCreated.php index efeb308d9bc9..da3a16d24689 100644 --- a/app/Events/ExpenseWasCreated.php +++ b/app/Events/ExpenseWasCreated.php @@ -5,7 +5,7 @@ use App\Events\Event; use Illuminate\Queue\SerializesModels; class ExpenseWasCreated extends Event { - + // Expenses use SerializesModels; public $expense; diff --git a/app/Events/ExpenseWasDeleted.php b/app/Events/ExpenseWasDeleted.php index a058cf1666e1..1549b483b497 100644 --- a/app/Events/ExpenseWasDeleted.php +++ b/app/Events/ExpenseWasDeleted.php @@ -5,7 +5,7 @@ use App\Events\Event; use Illuminate\Queue\SerializesModels; class ExpenseWasDeleted extends Event { - + // Expenses use SerializesModels; public $expense; diff --git a/app/Events/ExpenseWasRestored.php b/app/Events/ExpenseWasRestored.php index ca308e4ca0f8..b52a2d119a2d 100644 --- a/app/Events/ExpenseWasRestored.php +++ b/app/Events/ExpenseWasRestored.php @@ -5,7 +5,7 @@ use App\Events\Event; use Illuminate\Queue\SerializesModels; class ExpenseWasRestored extends Event { - + // Expenses use SerializesModels; public $expense; diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 8ecb5184ccd4..72dc2a4073b7 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -17,6 +17,7 @@ use App\Http\Requests\CreateExpenseRequest; class ExpenseController extends BaseController { + // Expenses protected $expenseRepo; protected $expenseService; diff --git a/app/Http/Requests/CreateExpenseRequest.php b/app/Http/Requests/CreateExpenseRequest.php index 0a0c71dd8208..a2c08cdfb8d7 100644 --- a/app/Http/Requests/CreateExpenseRequest.php +++ b/app/Http/Requests/CreateExpenseRequest.php @@ -5,6 +5,7 @@ use Illuminate\Validation\Factory; class CreateExpenseRequest extends Request { + // Expenses /** * Determine if the user is authorized to make this request. * diff --git a/app/Http/Requests/UpdateExpenseRequest.php b/app/Http/Requests/UpdateExpenseRequest.php index 83b192280849..6ee77877e6c0 100644 --- a/app/Http/Requests/UpdateExpenseRequest.php +++ b/app/Http/Requests/UpdateExpenseRequest.php @@ -2,9 +2,9 @@ use App\Http\Requests\Request; use Illuminate\Validation\Factory; -use App\Models\Invoice; -class UpdatePaymentRequest extends Request + +class UpdateExpenseRequest extends Request { /** * Determine if the user is authorized to make this request. diff --git a/app/Listeners/ExpenseListener.php b/app/Listeners/ExpenseListener.php index 8d02f00ff4db..0bccaffdb0ae 100644 --- a/app/Listeners/ExpenseListener.php +++ b/app/Listeners/ExpenseListener.php @@ -7,6 +7,7 @@ use App\Ninja\Repositories\ExpenseRepository; class ExpenseListener { + // Expenses protected $expenseRepo; public function __construct(ExpenseRepository $expenseRepo) diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 6376ac016f99..675ad96bcbed 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -6,6 +6,7 @@ use App\Events\ExpenseWasCreated; class Expense extends EntityModel { + // Expenses use SoftDeletes; use PresentableTrait; diff --git a/app/Models/ExpenseActivity.php b/app/Models/ExpenseActivity.php index 14f93e06c2f8..ddd23ab13e75 100644 --- a/app/Models/ExpenseActivity.php +++ b/app/Models/ExpenseActivity.php @@ -8,7 +8,7 @@ use Request; use Carbon; class ExpenseActivity extends Eloquent { - + // Expenses public $timestamps = true; public function scopeScope($query) diff --git a/app/Ninja/Presenters/ExpensePresenter.php b/app/Ninja/Presenters/ExpensePresenter.php index 72da1cfef2f1..3c7237d39d64 100644 --- a/app/Ninja/Presenters/ExpensePresenter.php +++ b/app/Ninja/Presenters/ExpensePresenter.php @@ -5,6 +5,7 @@ use Laracasts\Presenter\Presenter; class ExpensePresenter extends Presenter { + // Expenses public function vendor() { return $this->entity->vendor ? $this->entity->vendor->getDisplayName() : ''; diff --git a/app/Ninja/Repositories/ExpenseActivityRepository.php b/app/Ninja/Repositories/ExpenseActivityRepository.php index aa9dc7e9823e..db4fcdd8b777 100644 --- a/app/Ninja/Repositories/ExpenseActivityRepository.php +++ b/app/Ninja/Repositories/ExpenseActivityRepository.php @@ -10,6 +10,7 @@ use App\Models\Expense; class ExpenseActivityRepository { + // Expenses public function create(Expense $entity, $activityTypeId) { // init activity and copy over context diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 2cfaa600c062..5d87fea4d9d1 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -8,6 +8,7 @@ use App\Ninja\Repositories\BaseRepository; class ExpenseRepository extends BaseRepository { + // Expenses public function getClassName() { return 'App\Models\Expense'; diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index 92ffc85306ce..f316ecaabc99 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -8,6 +8,7 @@ use App\Ninja\Repositories\ExpenseRepository; class ExpenseService extends BaseService { + // Expenses protected $expenseRepo; protected $datatableService; diff --git a/database/migrations/2016_01_06_155001_create_expenses_table.php b/database/migrations/2016_01_06_155001_create_expenses_table.php index 56e74fbc7963..9a8a654f041a 100644 --- a/database/migrations/2016_01_06_155001_create_expenses_table.php +++ b/database/migrations/2016_01_06_155001_create_expenses_table.php @@ -5,6 +5,7 @@ use Illuminate\Database\Migrations\Migration; class CreateExpensesTable extends Migration { + // Expenses model /** * Run the migrations. * diff --git a/database/migrations/2016_01_06_191912_create_expense_activities_table.php b/database/migrations/2016_01_06_191912_create_expense_activities_table.php index a7106568cebe..9b5981741c25 100644 --- a/database/migrations/2016_01_06_191912_create_expense_activities_table.php +++ b/database/migrations/2016_01_06_191912_create_expense_activities_table.php @@ -5,6 +5,7 @@ use Illuminate\Database\Migrations\Migration; class CreateExpenseActivitiesTable extends Migration { + // Expenses model /** * Run the migrations. * diff --git a/invoiceninja.komodoproject b/invoiceninja.komodoproject new file mode 100644 index 000000000000..9f6ba7068341 --- /dev/null +++ b/invoiceninja.komodoproject @@ -0,0 +1,43 @@ + + + + + + + + + + + + + PHP + + + + application/x-www-form-urlencoded + GET + 1 + 0 + 0 + + + + + PHP + %25d/%25m/%25Y %25H:%25M:%25S + 1 + + default + default + PHP + Project + None + None + None + c:\wamp\bin\php\php5.6.15\php.exe + C:\webdev\invoiceninja;C:\webdev\invoiceninja\lavarelcodecomplete + 1 + None + None + + From eed8ee55a1aafb26d5022069fa1211d1a18e5901 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Thu, 7 Jan 2016 16:21:13 +0100 Subject: [PATCH 09/20] Vendor --- app/Events/VendorWasArchived.php | 1 + app/Events/VendorWasCreated.php | 1 + app/Events/VendorWasDeleted.php | 1 + app/Events/VendorWasRestored.php | 1 + app/Events/VendorWasUpdated.php | 2 +- app/Http/Controllers/PublicVendorController.php | 2 +- app/Http/Controllers/VendorActivityController.php | 2 +- app/Http/Controllers/VendorApiController.php | 2 +- app/Http/Controllers/VendorController.php | 2 +- app/Http/Requests/CreateVendorRequest.php | 2 +- app/Http/Requests/UpdateVendorRequest.php | 2 +- app/Listeners/VendorActivityListener.php | 2 +- app/Models/VendorActivity.php | 2 +- app/Models/VendorContact.php | 2 +- app/Models/VendorInvitation.php | 2 +- app/Ninja/Import/CSV/VendorTransformer.php | 2 +- app/Ninja/Import/FreshBooks/VendorTransformer.php | 2 +- app/Ninja/Import/Harvest/VendorContactTransformer.php | 2 +- app/Ninja/Import/Harvest/VendorTransformer.php | 2 +- app/Ninja/Import/Hiveage/VendorTransformer.php | 2 +- app/Ninja/Import/Invoiceable/VendorTransformer.php | 2 +- app/Ninja/Import/Nutcache/VendorTransformer.php | 2 +- app/Ninja/Import/Ronin/VendorTransformer.php | 2 +- app/Ninja/Import/Wave/VendorTransformer.php | 2 +- app/Ninja/Import/Zoho/VendorTransformer.php | 2 +- app/Ninja/Mailers/VendorContactMailer.php | 2 +- app/Ninja/Presenters/VendorPresenter.php | 2 +- app/Ninja/Repositories/VendorActivityRepository.php | 2 +- app/Ninja/Repositories/VendorContactRepository.php | 2 +- app/Ninja/Repositories/VendorRepository.php | 2 +- app/Ninja/Transformers/VendorContactTransformer.php | 2 +- app/Ninja/Transformers/VendorTransformer.php | 1 + app/Services/VendorActivityService.php | 2 +- app/Services/VendorService.php | 2 +- database/migrations/2016_01_04_175228_create_vendors_table.php | 2 +- .../2016_01_05_134713_create_vendorcontacts_table.php | 2 +- .../2016_01_06_101343_create_table_Vendor_Invitations.php | 2 +- .../2016_01_06_102020_create_table_Vendor_Activities.php | 2 +- resources/views/vendor.blade.php | 2 +- 39 files changed, 39 insertions(+), 34 deletions(-) diff --git a/app/Events/VendorWasArchived.php b/app/Events/VendorWasArchived.php index 65622c748f0c..ca268441f0d4 100644 --- a/app/Events/VendorWasArchived.php +++ b/app/Events/VendorWasArchived.php @@ -5,6 +5,7 @@ use Illuminate\Queue\SerializesModels; class VendorWasArchived extends Event { + // vendor use SerializesModels; public $vendor; diff --git a/app/Events/VendorWasCreated.php b/app/Events/VendorWasCreated.php index c52445d59974..b2d7e81c9394 100644 --- a/app/Events/VendorWasCreated.php +++ b/app/Events/VendorWasCreated.php @@ -5,6 +5,7 @@ use Illuminate\Queue\SerializesModels; class VendorWasCreated extends Event { + // vendor use SerializesModels; public $vendor; diff --git a/app/Events/VendorWasDeleted.php b/app/Events/VendorWasDeleted.php index 3f889bcad231..553bece3ccdc 100644 --- a/app/Events/VendorWasDeleted.php +++ b/app/Events/VendorWasDeleted.php @@ -5,6 +5,7 @@ use Illuminate\Queue\SerializesModels; class VendorWasDeleted extends Event { + // vendor use SerializesModels; public $vendor; diff --git a/app/Events/VendorWasRestored.php b/app/Events/VendorWasRestored.php index b7d350656bae..88c24693e611 100644 --- a/app/Events/VendorWasRestored.php +++ b/app/Events/VendorWasRestored.php @@ -5,6 +5,7 @@ use Illuminate\Queue\SerializesModels; class VendorWasRestored extends Event { + // vendor use SerializesModels; public $vendor; diff --git a/app/Events/VendorWasUpdated.php b/app/Events/VendorWasUpdated.php index 44d489f9c35d..eb90a68f46c0 100644 --- a/app/Events/VendorWasUpdated.php +++ b/app/Events/VendorWasUpdated.php @@ -1,5 +1,5 @@
    From d8a501ed9516a498cc592762fafa17431cb44bc0 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Thu, 7 Jan 2016 20:39:51 +0100 Subject: [PATCH 10/20] Posibility to modify payment terms --- app/Http/Controllers/AccountController.php | 16 +++ .../Controllers/PaymentTermController.php | 103 ++++++++++++++++++ .../Requests/CreatePaymentTermRequest.php | 30 +++++ app/Http/Requests/UpdateExpenseRequest.php | 4 +- app/Http/Requests/UpdatePaymentRequest.php | 6 +- .../Requests/UpdatePaymentTermRequest.php | 30 +++++ app/Http/routes.php | 10 +- app/Models/Account.php | 1 + app/Models/PaymentTerm.php | 15 ++- .../Repositories/PaymentTermRepository.php | 22 ++++ app/Services/PaymentTermService.php | 60 ++++++++++ database/.gitignore | 1 + ...1_07_173015_add_fields_to_paymentterms.php | 50 +++++++++ resources/lang/en/texts.php | 7 ++ .../views/accounts/payment_term.blade.php | 47 ++++++++ .../views/accounts/payment_terms.blade.php | 33 ++++++ 16 files changed, 426 insertions(+), 9 deletions(-) create mode 100644 app/Http/Controllers/PaymentTermController.php create mode 100644 app/Http/Requests/CreatePaymentTermRequest.php create mode 100644 app/Http/Requests/UpdatePaymentTermRequest.php create mode 100644 app/Ninja/Repositories/PaymentTermRepository.php create mode 100644 app/Services/PaymentTermService.php create mode 100644 database/migrations/2016_01_07_173015_add_fields_to_paymentterms.php create mode 100644 resources/views/accounts/payment_term.blade.php create mode 100644 resources/views/accounts/payment_terms.blade.php diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index dc890d7ed510..af9737caa42b 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -20,6 +20,7 @@ use App\Models\Account; use App\Models\Gateway; use App\Models\InvoiceDesign; use App\Models\TaxRate; +use App\Models\PaymentTerm; use App\Ninja\Repositories\AccountRepository; use App\Ninja\Repositories\ReferralRepository; use App\Ninja\Mailers\UserMailer; @@ -158,6 +159,8 @@ class AccountController extends BaseController return self::showProducts(); } elseif ($section === ACCOUNT_TAX_RATES) { return self::showTaxRates(); + } elseif ($section === ACCOUNT_PAYMENT_TERMS) { + return self::showPaymentTerms(); } elseif ($section === ACCOUNT_SYSTEM_SETTINGS) { return self::showSystemSettings(); } else { @@ -313,6 +316,17 @@ class AccountController extends BaseController return View::make('accounts.tax_rates', $data); } + private function showPaymentTerms() + { + $data = [ + 'account' => Auth::user()->account, + 'title' => trans('texts.payment_terms'), + 'taxRates' => PaymentTerm::scope()->get(['id', 'name', 'num_days']), + ]; + + return View::make('accounts.payment_terms', $data); + } + private function showInvoiceDesign($section) { $account = Auth::user()->account->load('country'); @@ -443,6 +457,8 @@ class AccountController extends BaseController return AccountController::saveProducts(); } elseif ($section === ACCOUNT_TAX_RATES) { return AccountController::saveTaxRates(); + } elseif ($section === ACCOUNT_PAYMENT_TERMS) { + return AccountController::savePaymetTerms(); } } diff --git a/app/Http/Controllers/PaymentTermController.php b/app/Http/Controllers/PaymentTermController.php new file mode 100644 index 000000000000..623ca1bf42da --- /dev/null +++ b/app/Http/Controllers/PaymentTermController.php @@ -0,0 +1,103 @@ +paymentTermService = $paymentTermService; + } + + public function index() + { + return Redirect::to('settings/' . ACCOUNT_PAYMENT_TERMS); + } + + public function getDatatable() + { + return $this->paymentTermService->getDatatable(); + } + + public function edit($publicId) + { + $data = [ + 'paymentTerm' => PaymentTerm::scope($publicId)->firstOrFail(), + 'method' => 'PUT', + 'url' => 'payment_terms/'.$publicId, + 'title' => trans('texts.edit_payment_term'), + ]; + + return View::make('accounts.payment_term', $data); + } + + public function create() + { + $data = [ + 'paymentTerm' => null, + 'method' => 'POST', + 'url' => 'payment_terms', + 'title' => trans('texts.create_payment_term'), + ]; + + return View::make('accounts.payment_term', $data); + } + + public function store() + { + return $this->save(); + } + + public function update($publicId) + { + return $this->save($publicId); + } + + private function save($publicId = false) + { + if ($publicId) { + $paymentTerm = PaymentTerm::scope($publicId)->firstOrFail(); + } else { + $paymentTerm = PaymentTerm::createNew(); + } + + $paymentTerm->name = trim(Input::get('name')); + $paymentTerm->num_days = Utils::parseInt(Input::get('num_days')); + $paymentTerm->save(); + + $message = $publicId ? trans('texts.updated_payment_term') : trans('texts.created_payment_term'); + Session::flash('message', $message); + + return Redirect::to('settings/' . ACCOUNT_PAYMENT_TERMS); + } + + public function bulk() + { + $action = Input::get('bulk_action'); + $ids = Input::get('bulk_public_id'); + $count = $this->paymentTermService->bulk($ids, $action); + + Session::flash('message', trans('texts.archived_payment_term')); + + return Redirect::to('settings/' . ACCOUNT_PAYMENT_TERMS); + } + +} diff --git a/app/Http/Requests/CreatePaymentTermRequest.php b/app/Http/Requests/CreatePaymentTermRequest.php new file mode 100644 index 000000000000..d8581793160e --- /dev/null +++ b/app/Http/Requests/CreatePaymentTermRequest.php @@ -0,0 +1,30 @@ + 'required', + 'name' => 'required', + ]; + } +} diff --git a/app/Http/Requests/UpdateExpenseRequest.php b/app/Http/Requests/UpdateExpenseRequest.php index 6ee77877e6c0..08555aaaaad4 100644 --- a/app/Http/Requests/UpdateExpenseRequest.php +++ b/app/Http/Requests/UpdateExpenseRequest.php @@ -23,6 +23,8 @@ class UpdateExpenseRequest extends Request */ public function rules() { - return []; + return [ + 'amount' => 'required|positive', + ]; } } diff --git a/app/Http/Requests/UpdatePaymentRequest.php b/app/Http/Requests/UpdatePaymentRequest.php index b3d4f536bc6e..29ac70e85e74 100644 --- a/app/Http/Requests/UpdatePaymentRequest.php +++ b/app/Http/Requests/UpdatePaymentRequest.php @@ -3,7 +3,7 @@ use App\Http\Requests\Request; use Illuminate\Validation\Factory; -class UpdateExpenseRequest extends Request +class UpdatePaymentRequest extends Request { /** * Determine if the user is authorized to make this request. @@ -22,9 +22,7 @@ class UpdateExpenseRequest extends Request */ public function rules() { - return [ - 'amount' => 'required|positive', - ]; + return []; } } diff --git a/app/Http/Requests/UpdatePaymentTermRequest.php b/app/Http/Requests/UpdatePaymentTermRequest.php new file mode 100644 index 000000000000..b3d4f536bc6e --- /dev/null +++ b/app/Http/Requests/UpdatePaymentTermRequest.php @@ -0,0 +1,30 @@ + 'required|positive', + ]; + + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index c94f16641abe..707aacff12e9 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -119,6 +119,12 @@ Route::group(['middleware' => 'auth'], function() { Route::get('settings/{section?}', 'AccountController@showSection'); Route::post('settings/{section?}', 'AccountController@doSection'); + // Payment term + Route::get('api/payment_terms', array('as'=>'api.payment_terms', 'uses'=>'PaymentTermController@getDatatable')); + Route::resource('payment_terms', 'PaymentTermController'); + Route::post('payment_terms/bulk', 'PaymentTermController@bulk'); + + Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData')); Route::post('user/setTheme', 'UserController@setTheme'); Route::post('remove_logo', 'AccountController@removeLogo'); @@ -278,6 +284,7 @@ if (!defined('CONTACT_EMAIL')) { define('ENTITY_ACTIVITY', 'activity'); define('ENTITY_VENDOR','vendor'); define('ENTITY_EXPENSE', 'expense'); + define('ENTITY_PAYMENT_TERM','payment_term'); define('PERSON_CONTACT', 'contact'); define('PERSON_USER', 'user'); @@ -308,6 +315,7 @@ if (!defined('CONTACT_EMAIL')) { define('ACCOUNT_API_TOKENS', 'api_tokens'); define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design'); define('ACCOUNT_SYSTEM_SETTINGS', 'system_settings'); + define('ACCOUNT_PAYMENT_TERMS','payment_terms'); define('ACTION_RESTORE', 'restore'); define('ACTION_ARCHIVE', 'archive'); @@ -568,7 +576,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/Account.php b/app/Models/Account.php index ae8276a48dd8..a089726b4958 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -30,6 +30,7 @@ class Account extends Eloquent ACCOUNT_PRODUCTS, ACCOUNT_NOTIFICATIONS, ACCOUNT_IMPORT_EXPORT, + ACCOUNT_PAYMENT_TERMS, ]; public static $advancedSettings = [ diff --git a/app/Models/PaymentTerm.php b/app/Models/PaymentTerm.php index de8cced5db72..dbb788aef1c2 100644 --- a/app/Models/PaymentTerm.php +++ b/app/Models/PaymentTerm.php @@ -1,8 +1,17 @@ where('payment_terms.account_id', '=', $accountId) + ->where('payment_terms.deleted_at', '=', null) + ->select('payment_terms.public_id', 'payment_terms.name', 'payment_terms.num_days', 'payment_terms.deleted_at'); + } +} diff --git a/app/Services/PaymentTermService.php b/app/Services/PaymentTermService.php new file mode 100644 index 000000000000..bfcf670475b9 --- /dev/null +++ b/app/Services/PaymentTermService.php @@ -0,0 +1,60 @@ +paymentTermRepo = $paymentTermRepo; + $this->datatableService = $datatableService; + } + + protected function getRepo() + { + return $this->paymentTermRepo; + } + + public function getDatatable($accountId = 0) + { + $query = $this->paymentTermRepo->find(); + + return $this->createDatatable(ENTITY_PAYMENT_TERM, $query, false); + } + + protected function getDatatableColumns($entityType, $hideClient) + { + return [ + [ + 'name', + function ($model) { + return link_to("payment_terms/{$model->public_id}/edit", $model->name); + } + ], + [ + 'days', + function ($model) { + return $model->num_days; + } + ] + ]; + } + + protected function getDatatableActions($entityType) + { + return [ + [ + uctrans('texts.edit_payment_terms'), + function ($model) { + return URL::to("payment_terms/{$model->public_id}/edit"); + } + ] + ]; + } +} \ No newline at end of file diff --git a/database/.gitignore b/database/.gitignore index 9b1dffd90fdc..bb55a4b89e24 100644 --- a/database/.gitignore +++ b/database/.gitignore @@ -1 +1,2 @@ *.sqlite +*-komodoproject \ No newline at end of file diff --git a/database/migrations/2016_01_07_173015_add_fields_to_paymentterms.php b/database/migrations/2016_01_07_173015_add_fields_to_paymentterms.php new file mode 100644 index 000000000000..1756cc60b246 --- /dev/null +++ b/database/migrations/2016_01_07_173015_add_fields_to_paymentterms.php @@ -0,0 +1,50 @@ +timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->integer('account_id', false, true); + $table->integer('public_id')->default(0); + }); + + // Update public id + $paymentTerms = DB::table('payment_terms') + ->where('public_id', '=',0) + ->select('id', 'public_id') + ->get(); + $i = 1; + foreach ($paymentTerms as $pTerm) { + $data = ['public_id' => $i]; + + DB::table('paymet_terms')->where('id', $pTerm->id)->update($data); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('payment_terms', function(Blueprint $table) + { + // + }); + } + +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index e7993b59ac58..be1067c43a3c 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1032,4 +1032,11 @@ return array( 'expense_date' => 'Expense date', 'expense_exchange_rate_100' => 'The amount for 100 in company currency', 'expense_should_be_invoiced' => 'Should this expense be invoiced?', + + // Payment terms + 'num_days' => 'Number of days', + 'create_payment_term' => 'Create payment term', + 'edit_payment_terms' => 'Edit payment term', + 'edit_payment_term' => 'Edit payment term', + 'archive_payment_term' => 'Archive payment term', ); diff --git a/resources/views/accounts/payment_term.blade.php b/resources/views/accounts/payment_term.blade.php new file mode 100644 index 000000000000..a1939995dcbb --- /dev/null +++ b/resources/views/accounts/payment_term.blade.php @@ -0,0 +1,47 @@ +@extends('header') + +@section('content') + @parent + + @include('accounts.nav', ['selected' => ACCOUNT_PAYMENT_TERMS]) + + {!! Former::open($url)->method($method) + ->rules([ + 'name' => 'required', + 'num_days' => 'required' + ]) + ->addClass('warn-on-exit') !!} + + +
    +
    +

    {!! $title !!}

    +
    +
    + + @if ($paymentTerm) + {{ Former::populate($paymentTerm) }} + @endif + + {!! Former::text('name')->label('texts.name') !!} + {!! Former::text('num_days')->label('texts.num_days') !!} + +
    +
    + + {!! Former::actions( + Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/payment_terms'))->appendIcon(Icon::create('remove-circle')), + Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) + ) !!} + + {!! Former::close() !!} + + + +@stop \ No newline at end of file diff --git a/resources/views/accounts/payment_terms.blade.php b/resources/views/accounts/payment_terms.blade.php new file mode 100644 index 000000000000..f88016991f13 --- /dev/null +++ b/resources/views/accounts/payment_terms.blade.php @@ -0,0 +1,33 @@ +@extends('header') + +@section('content') + @parent + + @include('accounts.nav', ['selected' => ACCOUNT_PAYMENT_TERMS]) + + {!! Button::primary(trans('texts.create_payment_term')) + ->asLinkTo(URL::to('/payment_terms/create')) + ->withAttributes(['class' => 'pull-right']) + ->appendIcon(Icon::create('plus-sign')) !!} + + @include('partials.bulk_form', ['entityType' => ENTITY_PAYMENT_TERM]) + + {!! Datatable::table() + ->addColumn( + trans('texts.name'), + trans('texts.num_days'), + trans('texts.action')) + ->setUrl(url('api/payment_terms/')) + ->setOptions('sPaginationType', 'bootstrap') + ->setOptions('bFilter', false) + ->setOptions('bAutoWidth', false) + ->setOptions('aoColumns', [[ "sWidth"=> "40%" ], [ "sWidth"=> "40%" ], ["sWidth"=> "20%"]]) + ->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]]) + ->render('datatable') !!} + + + + +@stop \ No newline at end of file From d89dc2e8271f001617b5f45dc68eeb07f329be36 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Fri, 8 Jan 2016 19:01:00 +0100 Subject: [PATCH 11/20] Expenses --- app/Events/ExpenseWasArchived.php | 6 +- app/Events/ExpenseWasCreated.php | 5 +- app/Events/ExpenseWasUpdated.php | 21 +++ app/Http/Controllers/DashboardController.php | 8 ++ .../Controllers/ExpenseActivityController.php | 27 ++++ app/Http/Controllers/ExpenseController.php | 79 ++++++++++-- app/Http/Controllers/VendorController.php | 3 +- app/Http/Requests/UpdateExpenseRequest.php | 2 + app/Http/routes.php | 2 + app/Listeners/ExpenseActivityListener.php | 57 ++++++++ app/Listeners/VendorActivityListener.php | 8 +- app/Models/Expense.php | 23 +++- app/Models/ExpenseActivity.php | 7 +- app/Models/Vendor.php | 46 ++----- .../ExpenseActivityRepository.php | 35 ++--- app/Ninja/Repositories/ExpenseRepository.php | 87 ++++++------- app/Services/ExpenseActivityService.php | 62 +++++++++ app/Services/ExpenseService.php | 57 +++++--- ...016_01_06_155001_create_expenses_table.php | 4 +- resources/lang/en/texts.php | 15 +++ resources/views/dashboard.blade.php | 7 +- resources/views/expenses/edit.blade.php | 82 ++++++++++++ resources/views/expenses/show.blade.php | 122 ++++++++++++++++++ 23 files changed, 610 insertions(+), 155 deletions(-) create mode 100644 app/Events/ExpenseWasUpdated.php create mode 100644 app/Http/Controllers/ExpenseActivityController.php create mode 100644 app/Listeners/ExpenseActivityListener.php create mode 100644 app/Services/ExpenseActivityService.php create mode 100644 resources/views/expenses/edit.blade.php create mode 100644 resources/views/expenses/show.blade.php diff --git a/app/Events/ExpenseWasArchived.php b/app/Events/ExpenseWasArchived.php index fe27998d29c4..c993d5e09632 100644 --- a/app/Events/ExpenseWasArchived.php +++ b/app/Events/ExpenseWasArchived.php @@ -1,12 +1,10 @@ expense = $expense; + } +} diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 847afbf7d574..cbf04ef79099 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -7,6 +7,7 @@ use App\Models\Activity; use App\Models\Invoice; use App\Models\Payment; use App\Models\VendorActivity; +use App\Models\ExpenseActivity; class DashboardController extends BaseController { @@ -76,6 +77,12 @@ class DashboardController extends BaseController ->take(50) ->get(); + $expenseactivities = ExpenseActivity::where('expense_activities.account_id', '=', Auth::user()->account_id) + ->where('activity_type_id', '>', 0) + ->orderBy('created_at', 'desc') + ->take(50) + ->get(); + $pastDue = DB::table('invoices') ->leftJoin('clients', 'clients.id', '=', 'invoices.client_id') ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id') @@ -150,6 +157,7 @@ class DashboardController extends BaseController 'title' => trans('texts.dashboard'), 'hasQuotes' => $hasQuotes, 'vendoractivities' => $vendoractivities, + 'expenseactivities' => $expenseactivities, ]; return View::make('dashboard', $data); diff --git a/app/Http/Controllers/ExpenseActivityController.php b/app/Http/Controllers/ExpenseActivityController.php new file mode 100644 index 000000000000..0993123129c5 --- /dev/null +++ b/app/Http/Controllers/ExpenseActivityController.php @@ -0,0 +1,27 @@ +activityService = $activityService; + } + + public function getDatatable($vendorPublicId) + { + return $this->activityService->getDatatable($vendorPublicId); + } +} diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 72dc2a4073b7..033be0743117 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -1,5 +1,7 @@ ENTITY_EXPENSE, 'title' => trans('texts.expenses'), - 'sortCol' => '4', + 'sortCol' => '1', 'columns' => Utils::trans([ - 'checkbox', 'vendor', 'expense_amount', - 'expense_balance', 'expense_date', - 'private_notes', 'public_notes', - '' + 'is_invoiced', + 'should_be_invoiced', + 'expense' ]), )); } - public function getDatatable($vendorPublicId = null) + public function getDatatable($expensePublicId = null) { - return $this->expenseService->getDatatable($vendorPublicId, Input::get('sSearch')); + return $this->expenseService->getDatatable($expensePublicId, Input::get('sSearch')); } public function create($vendorPublicId = 0) { - $vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail(); + 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(), + 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendor' => $vendor, ); $data = array_merge($data, self::getViewModel()); @@ -86,11 +94,35 @@ class ExpenseController extends BaseController 'method' => 'PUT', 'url' => 'expenses/'.$publicId, 'title' => 'Edit Expense', - 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), ); + 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendorPublicId' => $expense->vendor_id); - return View::make('expense.edit', $data); + $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()); @@ -129,5 +161,28 @@ class ExpenseController extends BaseController '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/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 94bd11768390..7fac33b1f840 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -56,7 +56,6 @@ class VendorController extends BaseController 'contact', 'email', 'date_created', - //'last_login', 'balance', '' ]), @@ -94,7 +93,7 @@ class VendorController extends BaseController Utils::trackViewed($vendor->getDisplayName(), 'vendor'); $actionLinks = [ - ['label' => trans('texts.new_expense'), 'url' => '/expenses/create/'.$vendor->public_id] + ['label' => trans('texts.new_vendor'), 'url' => '/vendors/create/'.$vendor->public_id] ]; $data = array( diff --git a/app/Http/Requests/UpdateExpenseRequest.php b/app/Http/Requests/UpdateExpenseRequest.php index 08555aaaaad4..9170e9df02ab 100644 --- a/app/Http/Requests/UpdateExpenseRequest.php +++ b/app/Http/Requests/UpdateExpenseRequest.php @@ -25,6 +25,8 @@ class UpdateExpenseRequest extends Request { return [ 'amount' => 'required|positive', + 'public_notes' => 'required', + 'expense_date' => 'required', ]; } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 707aacff12e9..5df598507212 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -283,8 +283,10 @@ if (!defined('CONTACT_EMAIL')) { define('ENTITY_PRODUCT', 'product'); define('ENTITY_ACTIVITY', 'activity'); define('ENTITY_VENDOR','vendor'); + define('ENTITY_VENDOR_ACTIVITY','vendor_activity'); define('ENTITY_EXPENSE', 'expense'); define('ENTITY_PAYMENT_TERM','payment_term'); + define('ENTITY_EXPENSE_ACTIVITY','expense_activity'); define('PERSON_CONTACT', 'contact'); define('PERSON_USER', 'user'); diff --git a/app/Listeners/ExpenseActivityListener.php b/app/Listeners/ExpenseActivityListener.php new file mode 100644 index 000000000000..5e3cf1d9e705 --- /dev/null +++ b/app/Listeners/ExpenseActivityListener.php @@ -0,0 +1,57 @@ +activityRepo = $activityRepo; + } + + // Expenses + public function createdExpense(ExpenseWasCreated $event) + { + $this->activityRepo->create( + $event->expense, + ACTIVITY_TYPE_CREATE_EXPENSE + ); + } + + public function deletedExpense(ExpenseWasDeleted $event) + { + $this->activityRepo->create( + $event->expense, + ACTIVITY_TYPE_DELETE_EXPENSE + ); + } + + public function archivedExpense(ExpenseWasArchived $event) + { + /* + if ($event->client->is_deleted) { + return; + } + */ + + $this->activityRepo->create( + $event->expense, + ACTIVITY_TYPE_ARCHIVE_EXPENSE + ); + } + + public function restoredExpense(ExpenseWasRestored $event) + { + $this->activityRepo->create( + $event->expense, + ACTIVITY_TYPE_RESTORE_EXPENSE + ); + } +} diff --git a/app/Listeners/VendorActivityListener.php b/app/Listeners/VendorActivityListener.php index 58b3725ec922..181b50feb8bb 100644 --- a/app/Listeners/VendorActivityListener.php +++ b/app/Listeners/VendorActivityListener.php @@ -29,19 +29,19 @@ class VendorActivityListener { $this->activityRepo->create( $event->vendor, - ACTIVITY_TYPE_DELETE_CLIENT + ACTIVITY_TYPE_DELETE_VENDOR ); } public function archivedVendor(VendorWasArchived $event) { - if ($event->client->is_deleted) { + if ($event->vendor->is_deleted) { return; } $this->activityRepo->create( $event->vendor, - ACTIVITY_TYPE_ARCHIVE_CLIENT + ACTIVITY_TYPE_ARCHIVE_VENDOR ); } @@ -49,7 +49,7 @@ class VendorActivityListener { $this->activityRepo->create( $event->vendor, - ACTIVITY_TYPE_RESTORE_CLIENT + ACTIVITY_TYPE_RESTORE_VENDOR ); } } diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 675ad96bcbed..a666f558cf0b 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -3,6 +3,7 @@ use Laracasts\Presenter\PresentableTrait; use Illuminate\Database\Eloquent\SoftDeletes; use App\Events\ExpenseWasCreated; +use App\Events\ExpenseWasUpdated; class Expense extends EntityModel { @@ -13,6 +14,13 @@ class Expense extends EntityModel protected $dates = ['deleted_at']; protected $presenter = 'App\Ninja\Presenters\ExpensePresenter'; + protected $fillable = [ + 'amount', + 'amount_cur', + 'exchange_rate', + 'private_notes', + 'public_notes', + ]; public function account() { return $this->belongsTo('App\Models\Account'); @@ -30,9 +38,22 @@ class Expense extends EntityModel public function getName() { - return ''; + if($this->expense_number) + return $this->expense_number; + + return $this->public_id; + } + + public function getDisplayName() + { + return $this->getName(); } + public function getRoute() + { + return "/expenses/{$this->public_id}"; + } + public function getEntityType() { return ENTITY_EXPENSE; diff --git a/app/Models/ExpenseActivity.php b/app/Models/ExpenseActivity.php index ddd23ab13e75..bd72098518c1 100644 --- a/app/Models/ExpenseActivity.php +++ b/app/Models/ExpenseActivity.php @@ -46,13 +46,12 @@ class ExpenseActivity extends Eloquent { $isSystem = $this->is_system; $expense = $this->expense; - if($expense) { - $route = link_to($expense->getRoute(), $vendor->getDisplayName()); + if($expense) + { + $route = link_to($expense->getRoute(), $expense->getDisplayName()); } else { $route ='no expense id'; } - - $data = [ 'expense' => $route, diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 690c34eecb65..c228c636011b 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -8,16 +8,14 @@ use App\Events\VendorWasUpdated; use Laracasts\Presenter\PresentableTrait; use Illuminate\Database\Eloquent\SoftDeletes; -class Vendor extends EntityModel { - +class Vendor extends EntityModel +{ use PresentableTrait; use SoftDeletes; - protected $presenter = 'App\Ninja\Presenters\VendorPresenter'; - - protected $dates = ['deleted_at']; - - protected $fillable = [ + protected $presenter = 'App\Ninja\Presenters\VendorPresenter'; + protected $dates = ['deleted_at']; + protected $fillable = [ 'name', 'id_number', 'vat_number', @@ -39,15 +37,15 @@ class Vendor extends EntityModel { 'website', ]; - public static $fieldName = 'name'; - public static $fieldPhone = 'work_phone'; - public static $fieldAddress1 = 'address1'; - public static $fieldAddress2 = 'address2'; - public static $fieldCity = 'city'; - public static $fieldState = 'state'; - public static $fieldPostalCode = 'postal_code'; - public static $fieldNotes = 'notes'; - public static $fieldCountry = 'country'; + public static $fieldName = 'name'; + public static $fieldPhone = 'work_phone'; + public static $fieldAddress1 = 'address1'; + public static $fieldAddress2 = 'address2'; + public static $fieldCity = 'city'; + public static $fieldState = 'state'; + public static $fieldPostalCode = 'postal_code'; + public static $fieldNotes = 'notes'; + public static $fieldCountry = 'country'; public static function getImportColumns() { @@ -165,13 +163,11 @@ class Vendor extends EntityModel { return "/vendors/{$this->public_id}"; } - public function getTotalCredit() { return 0; } - public function getName() { return $this->name; @@ -231,7 +227,6 @@ class Vendor extends EntityModel { } } - public function getGatewayToken() { $this->account->load('account_gateways'); @@ -269,19 +264,6 @@ class Vendor extends EntityModel { return $this->account->currency_id ?: DEFAULT_CURRENCY; } - - /* - public function getCounter($isQuote) - { - return $isQuote ? $this->quote_number_counter : $this->invoice_number_counter; - } - */ - - public function markLoggedIn() - { - //$this->last_login = Carbon::now()->toDateTimeString(); - $this->save(); - } } Vendor::creating(function ($vendor) { diff --git a/app/Ninja/Repositories/ExpenseActivityRepository.php b/app/Ninja/Repositories/ExpenseActivityRepository.php index db4fcdd8b777..e1baed24c857 100644 --- a/app/Ninja/Repositories/ExpenseActivityRepository.php +++ b/app/Ninja/Repositories/ExpenseActivityRepository.php @@ -46,36 +46,19 @@ class ExpenseActivityRepository } - public function findByVendorId($vendorId) + public function findByExpenseId($expenseId) { return DB::table('expense_activities') - ->join('accounts', 'accounts.id', '=', 'vendor_activities.account_id') - ->join('users', 'users.id', '=', 'vendor_activities.user_id') - ->join('vendors', 'vendors.id', '=', 'vendor_activities.vendor_id') - ->leftJoin('vendor_contacts', 'vendor_contacts.vendor_id', '=', 'vendors.id') - ->where('vendors.id', '=', $vendorId) - ->where('vendor_contacts.is_primary', '=', 1) - ->whereNull('vendor_contacts.deleted_at') - ->select( - DB::raw('COALESCE(vendors.currency_id, accounts.currency_id) currency_id'), - DB::raw('COALESCE(vendors.country_id, accounts.country_id) country_id'), - 'vendor_activities.id', - 'vendor_activities.created_at', - 'vendor_activities.contact_id', - 'vendor_activities.activity_type_id', - 'vendor_activities.is_system', - 'vendor_activities.balance', - 'vendor_activities.adjustment', + ->join('accounts', 'accounts.id', '=', 'expense_activities.account_id') + ->join('users', 'users.id', '=', 'expense_activities.user_id') + ->join('expenses','expenses.public_id', '=', 'expense_activities.expense_id') + ->where('expense_activities.expense_id', '=', $expenseId) + ->select('*', 'users.first_name as user_first_name', 'users.last_name as user_last_name', 'users.email as user_email', - 'vendors.name as vendor_name', - 'vendors.public_id as vendor_public_id', - 'vendor_contacts.id as contact', - 'vendor_contacts.first_name as first_name', - 'vendor_contacts.last_name as last_name', - 'vendor_contacts.email as email' - - ); + 'expenses.amount' + ); + } } \ No newline at end of file diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 5d87fea4d9d1..e35b7ae208a5 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -5,6 +5,7 @@ use Utils; use App\Models\Expense; use App\Models\Vendor; use App\Ninja\Repositories\BaseRepository; +use Session; class ExpenseRepository extends BaseRepository { @@ -25,62 +26,37 @@ class ExpenseRepository extends BaseRepository public function find($filter = null) { - /* - $query = DB::table('expenses') - ->join('accounts', 'accounts.id', '=', 'expenses.account_id') - ->join('vendors', 'vendors.id', '=', 'expenses.vendor_id') - ->join('vendor_contacts', 'vendor_contacts.vendor_id', '=', 'vendors.id') - ->where('vendors.account_id', '=', \Auth::user()->account_id) - ->where('vendors.deleted_at', '=', null) - ->where('vendor_contacts.deleted_at', '=', null) - ->where('vendor_contacts.is_primary', '=', true) - ->select( - DB::raw('COALESCE(vendors.currency_id, accounts.currency_id) currency_id'), - DB::raw('COALESCE(vendors.country_id, accounts.country_id) country_id'), - 'expenses.public_id', - 'vendors.name as vendor_name', - 'vendors.public_id as vendor_public_id', - 'expenses.amount', - 'expenses.balance', - 'expenses.expense_date', - 'vendor_contacts.first_name', - 'vendor_contacts.last_name', - 'vendor_contacts.email', - 'expenses.private_notes', - 'expenses.deleted_at', - 'expenses.is_deleted' - ); - */ $accountid = \Auth::user()->account_id; $query = DB::table('expenses') ->join('accounts', 'accounts.id', '=', 'expenses.account_id') - //->join('vendors', 'vendors.id', '=', 'expenses.vendor_id') ->where('expenses.account_id', '=', $accountid) ->where('expenses.deleted_at', '=', null) - ->select( - //DB::raw('COALESCE(vendors.currency_id, accounts.currency_id) currency_id'), - //DB::raw('COALESCE(vendors.country_id, accounts.country_id) country_id'), - 'expenses.public_id', - //'vendors.name as vendor_name', - //'vendors.public_id as vendor_public_id', + ->select('expenses.account_id', 'expenses.amount', - 'expenses.balance', - 'expenses.expense_date', - 'expenses.public_notes', + 'expenses.amount_cur', + 'expenses.currency_id', 'expenses.deleted_at', - 'expenses.is_deleted' - ); - + 'expenses.exchange_rate', + 'expenses.expense_date', + 'expenses.id', + 'expenses.is_deleted', + 'expenses.is_invoiced', + 'expenses.private_notes', + 'expenses.public_id', + 'expenses.public_notes', + 'expenses.should_be_invoiced', + 'expenses.vendor_id'); + if (!\Session::get('show_trash:expense')) { $query->where('expenses.deleted_at', '=', null); } -/* + if ($filter) { $query->where(function ($query) use ($filter) { - $query->where('vendors.name', 'like', '%'.$filter.'%'); + $query->where('expenses.public_notes', 'like', '%'.$filter.'%'); }); } -*/ + return $query; } @@ -94,26 +70,43 @@ class ExpenseRepository extends BaseRepository $expense = Expense::createNew(); } - // First auto fille + // First auto fill $expense->fill($input); // We can have an expense without a vendor if(isset($input['vendor'])) { - $expense->vendor_id = Vendor::getPrivateId($input['vendor']); + $expense->vendor_id = $input['vendor']; } $expense->expense_date = Utils::toSqlDate($input['expense_date']); $expense->amount = Utils::parseFloat($input['amount']); - if(isset($input['amountcur'])) - $expense->amountcur = Utils::parseFloat($input['amountcur']); + if(isset($input['amount_cur'])) + $expense->amount_cur = Utils::parseFloat($input['amount_cur']); - $expense->balance = Utils::parseFloat($input['amount']); $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; + + $expense->should_be_invoiced = isset($input['should_be_invoiced']) ? true : false; $expense->save(); return $expense; diff --git a/app/Services/ExpenseActivityService.php b/app/Services/ExpenseActivityService.php new file mode 100644 index 000000000000..bd75aac7d26a --- /dev/null +++ b/app/Services/ExpenseActivityService.php @@ -0,0 +1,62 @@ +activityRepo = $activityRepo; + $this->datatableService = $datatableService; + } + + public function getDatatable($expensePublicId = null) + { + $expenseId = Expense::getPrivateId($expensePublicId); + + $query = $this->activityRepo->findByExpenseId($expenseId); + + return $this->createDatatable(ENTITY_EXPENSE_ACTIVITY, $query); + } + + protected function getDatatableColumns($entityType, $hideExpense) + { + return [ + [ + 'expense_activities.id', + function ($model) { + return Utils::timestampToDateTimeString(strtotime($model->created_at)); + } + ], + [ + 'activity_type_id', + function ($model) { + $data = [ + 'expense' => link_to('/expenses/' . $model->public_id, trans('texts.view_expense',['expense' => $model->public_id])), + 'user' => $model->is_system ? '' . trans('texts.system') . '' : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email), + ]; + + return trans("texts.activity_{$model->activity_type_id}", $data); + } + ], + [ + 'amount', + function ($model) { + return Utils::formatMoney($model->amount); + } + ], + [ + 'expense_date', + function ($model) { + return Utils::fromSqlDate($model->expense_date); + } + ] + ]; + } +} \ No newline at end of file diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index f316ecaabc99..a9203d9338fb 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -1,5 +1,6 @@ vendor_public_id ? link_to("vendors/{$model->vendor_public_id}", Utils::getVendorDisplayName($model)) : ''; + [ + 'vendor_id', + function ($model) + { + if($model->vendor_id) { + + $vendors = DB::table('vendors')->where('public_id', '=',$model->vendor_id)->select('id', 'public_id','name')->get(); + // should only be one! + $vendor = $vendors[0]; + + if($vendor) { + return link_to("vendors/{$vendor->public_id}", $vendor->name); + } + return 'no vendor: ' . $model->vendor_id; + } else { + return 'No vendor:' ; + } }, - ! $hideClient - ],*/ + ], [ 'amount', function ($model) { - return Utils::formatMoney($model->amount, false, false) . ''; - } - ], - [ - 'balance', - function ($model) { - return Utils::formatMoney($model->balance, false, false); + return Utils::formatMoney($model->amount, false, false); } ], [ @@ -64,11 +71,29 @@ class ExpenseService extends BaseService } ], [ - 'private_public', + 'public_notes', function ($model) { - return $model->public_notes; + return $model->public_notes != null ? $model->public_notes : ''; } - ] + ], + [ + 'is_invoiced', + function ($model) { + return $model->is_invoiced ? trans('texts.expense_is_invoiced') : trans('texts.expense_is_not_invoiced'); + } + ], + [ + 'should_be_invoiced', + function ($model) { + 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', ['expense' => $model->public_id])); + } + ] ]; } /* diff --git a/database/migrations/2016_01_06_155001_create_expenses_table.php b/database/migrations/2016_01_06_155001_create_expenses_table.php index 9a8a654f041a..95d538b3a69d 100644 --- a/database/migrations/2016_01_06_155001_create_expenses_table.php +++ b/database/migrations/2016_01_06_155001_create_expenses_table.php @@ -27,11 +27,9 @@ class CreateExpensesTable extends Migration $table->boolean('is_deleted')->default(false); $table->decimal('amount', 13, 2); - $table->decimal('amountcur', 13, 2); + $table->decimal('amount_cur', 13, 2); $table->decimal('exchange_rate', 13, 2); - $table->decimal('balance', 13, 2); $table->date('expense_date')->nullable(); - $table->string('expense_number')->nullable(); $table->text('private_notes'); $table->text('public_notes'); $table->integer('currency_id',false, true)->nullable(); diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index be1067c43a3c..120f46d5d6a1 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1015,6 +1015,7 @@ return array( 'white_label_purchase_link' => 'Purchase a white label license', // Expense / vendor + 'expense' => 'Expense', 'expenses' => 'Expenses', 'new_expense' => 'Create expense', 'vendors' => 'Vendors', @@ -1032,6 +1033,20 @@ return array( 'expense_date' => 'Expense date', 'expense_exchange_rate_100' => 'The amount for 100 in company currency', 'expense_should_be_invoiced' => 'Should this expense be invoiced?', + 'public_notes' => 'Public notes', + 'expense_amount_in_cur' => 'Expense amount in curency', + 'is_invoiced' => 'Is invoiced', + 'expense_is_not_invoiced' => 'Expense not invoiced', + 'expense_is_invoiced' => 'Expense invoiced', + 'yes' => 'Yes', + 'no' => 'No', + 'should_be_invoiced' => 'Should be invoiced', + 'view_expense' => 'View expense # :expense', + 'edit_expense' => 'Edit expense', + 'archive_expense' => 'Archive expense', + 'delete_expense' => 'Delete expense', + 'view_expense_num' => 'Expense # :expense', + 'updated_expense' => 'Expense updated', // Payment terms 'num_days' => 'Number of days', diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index b3f1042509f8..d678aa53669f 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -90,7 +90,12 @@ {!! $activity->getMessage() !!} @endforeach - + @foreach ($expenseactivities as $activity) +
  • + {{ Utils::timestampToDateString(strtotime($activity->created_at)) }}: + {!! $activity->getMessage() !!} +
  • + @endforeach
    diff --git a/resources/views/expenses/edit.blade.php b/resources/views/expenses/edit.blade.php new file mode 100644 index 000000000000..031551717fc9 --- /dev/null +++ b/resources/views/expenses/edit.blade.php @@ -0,0 +1,82 @@ +@extends('header') + +@section('content') + + {!! Former::open($url)->addClass('col-md-10 col-md-offset-1 warn-on-exit')->method($method)->rules(array( + 'public_notes' => 'required', + 'amount' => 'required', + 'expense_date' => 'required', + )) !!} + + @if ($expense) + {!! Former::populate($expense) !!} + {!! Former::hidden('public_id') !!} + @endif + + +
    +
    + +
    +
    + {!! Former::select('vendor')->addOption('', '')->addGroupClass('client-select') !!} + {!! Former::text('expense_date') + ->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT)) + ->addGroupClass('expense_date')->label(trans('texts.expense_date')) + ->append('') !!} + {!! Former::text('amount')->label(trans('texts.expense_amount')) !!} + {!! Former::text('amount_cur')->label(trans('texts.expense_amount_in_cur')) !!} + {!! Former::select('currency_id')->addOption('','') + ->placeholder($account->currency ? $account->currency->name : '') + ->fromQuery($currencies, 'name', 'id') !!} + {!! Former::text('exchange_rate')->append(trans('texts.expense_exchange_rate_100')) !!} + {!! Former::textarea('private_notes') !!} + {!! Former::textarea('public_notes') !!} + {!! Former::checkbox('should_be_invoiced') !!} +
    +
    +
    +
    + +
    + {!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/credits'))->appendIcon(Icon::create('remove-circle')) !!} + {!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!} +
    + + {!! Former::close() !!} + + +@stop \ No newline at end of file diff --git a/resources/views/expenses/show.blade.php b/resources/views/expenses/show.blade.php new file mode 100644 index 000000000000..f8d83d97350b --- /dev/null +++ b/resources/views/expenses/show.blade.php @@ -0,0 +1,122 @@ +@extends('header') + +@section('head') + @parent +@stop + +@section('content') +
    + {!! Former::open('expenses/bulk')->addClass('mainForm') !!} +
    + {!! Former::text('action') !!} + {!! Former::text('public_id')->value($expense->public_id) !!} +
    + + @if ($expense->trashed()) + {!! Button::primary(trans('texts.restore_expense'))->withAttributes(['onclick' => 'onRestoreClick()']) !!} + @else + {!! DropdownButton::normal(trans('texts.edit_expense')) + ->withAttributes(['class'=>'normalDropDown']) + ->withContents([ + ['label' => trans('texts.archive_expense'), 'url' => "javascript:onArchiveClick()"], + ['label' => trans('texts.delete_expense'), 'url' => "javascript:onDeleteClick()"], + ] + )->split() !!} + + {!! DropdownButton::primary(trans('texts.new_expense')) + ->withAttributes(['class'=>'primaryDropDown']) + ->withContents($actionLinks)->split() !!} + @endif + {!! Former::close() !!} + +
    + +

    {{ trans('texts.view_expense_num', ['expense' => $expense->public_id]) }}

    +
    +
    +
    + +
    +

    {{ trans('texts.details') }}

    + +

    {{ $expense->public_notes }}

    +
    + +
    +

    {{ trans('texts.standing') }} + + + + + + + + + + @if ($credit > 0) + + + + + @endif +
    {{ trans('texts.expense_date') }}{{ Utils::fromSqlDate($expense->expense_date) }}
    {{ trans('texts.expense_amount') }}{{ Utils::formatMoney($expense->amount) }}
    {{ trans('texts.expense_amount_cur') }}{{ Utils::formatMoney($$expense->amount_cur, $expense->curency_id) }}
    +

    +
    +
    +
    +
    + + + +
    +
    + {!! Datatable::table() + ->addColumn( + trans('texts.date'), + trans('texts.message'), + trans('texts.balance'), + trans('texts.adjustment')) + ->setUrl(url('api/expenseactivities/'. $expense->public_id)) + ->setCustomValues('entityType', 'activity') + ->setOptions('sPaginationType', 'bootstrap') + ->setOptions('bFilter', false) + ->setOptions('aaSorting', [['0', 'desc']]) + ->render('datatable') !!} +
    +
    + + + +@stop From d0bfe8251285672bb8ab5cc8d0fb985b2ac605d5 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Sat, 9 Jan 2016 06:24:43 +0100 Subject: [PATCH 12/20] Expense module --- app/Events/ExpenseWasArchived.php | 2 +- app/Http/Controllers/ExpenseController.php | 10 ++++- app/Ninja/Repositories/ExpenseRepository.php | 22 ++++++++-- app/Services/ExpenseService.php | 44 +++++++++---------- config/debugbar.php | 2 +- ...016_01_06_155001_create_expenses_table.php | 9 ++-- resources/lang/en/texts.php | 6 ++- resources/views/expenses/edit.blade.php | 19 +++++++- resources/views/list.blade.php | 3 ++ 9 files changed, 79 insertions(+), 38 deletions(-) diff --git a/app/Events/ExpenseWasArchived.php b/app/Events/ExpenseWasArchived.php index c993d5e09632..a4b2af4bdf31 100644 --- a/app/Events/ExpenseWasArchived.php +++ b/app/Events/ExpenseWasArchived.php @@ -14,7 +14,7 @@ class ExpenseWasArchived extends Event * * @return void */ - public function __construct($espense) + public function __construct($expense) { $this->expense = $expense; } diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 033be0743117..2e59dbecd208 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -14,6 +14,7 @@ use Redirect; use Cache; use App\Models\Vendor; use App\Models\Expense; +use App\Models\Client; use App\Services\ExpenseService; use App\Ninja\Repositories\ExpenseRepository; use App\Http\Requests\CreateExpenseRequest; @@ -45,13 +46,14 @@ class ExpenseController extends BaseController 'title' => trans('texts.expenses'), 'sortCol' => '1', 'columns' => Utils::trans([ + 'checkbox', 'vendor', 'expense_amount', 'expense_date', 'public_notes', 'is_invoiced', 'should_be_invoiced', - 'expense' + '' ]), )); } @@ -76,6 +78,8 @@ class ExpenseController extends BaseController '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()); @@ -95,7 +99,9 @@ class ExpenseController extends BaseController 'url' => 'expenses/'.$publicId, 'title' => 'Edit Expense', 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), - 'vendorPublicId' => $expense->vendor_id); + 'vendorPublicId' => $expense->vendor_id, + 'clients' => Client::scope()->with('contacts')->orderBy('name')->get() + ); $data = array_merge($data, self::getViewModel()); diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index e35b7ae208a5..12133947b510 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -29,8 +29,8 @@ class ExpenseRepository extends BaseRepository $accountid = \Auth::user()->account_id; $query = DB::table('expenses') ->join('accounts', 'accounts.id', '=', 'expenses.account_id') + ->leftjoin('vendors','vendors.public_id','=', 'expenses.vendor_id') ->where('expenses.account_id', '=', $accountid) - ->where('expenses.deleted_at', '=', null) ->select('expenses.account_id', 'expenses.amount', 'expenses.amount_cur', @@ -45,12 +45,24 @@ class ExpenseRepository extends BaseRepository 'expenses.public_id', 'expenses.public_notes', 'expenses.should_be_invoiced', - 'expenses.vendor_id'); + 'expenses.vendor_id', + 'vendors.name as vendor_name', + '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.'%'); @@ -105,8 +117,10 @@ class ExpenseRepository extends BaseRepository // Calculate the amount cur $expense->amount_cur = ($expense->amount / 100) * $expense->exchange_rate; - $expense->should_be_invoiced = isset($input['should_be_invoiced']) ? true : false; + if(isset($input['client'])) { + $expense->invoice_client_id = $input['client']; + } $expense->save(); return $expense; diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index a9203d9338fb..46cbc2bb6e29 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -40,23 +40,15 @@ class ExpenseService extends BaseService { return [ [ - 'vendor_id', + 'vendor_name', function ($model) { - if($model->vendor_id) { - - $vendors = DB::table('vendors')->where('public_id', '=',$model->vendor_id)->select('id', 'public_id','name')->get(); - // should only be one! - $vendor = $vendors[0]; - - if($vendor) { - return link_to("vendors/{$vendor->public_id}", $vendor->name); - } - return 'no vendor: ' . $model->vendor_id; + if($model->vendor_public_id) { + return link_to("vendors/{$model->vendor_public_id}", $model->vendor_name); } else { - return 'No vendor:' ; + return 'No vendor' ; } - }, + } ], [ 'amount', @@ -79,7 +71,7 @@ class ExpenseService extends BaseService [ 'is_invoiced', function ($model) { - return $model->is_invoiced ? trans('texts.expense_is_invoiced') : trans('texts.expense_is_not_invoiced'); + return $model->is_invoiced ? trans('texts.yes') : trans('texts.no'); } ], [ @@ -88,25 +80,31 @@ 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', ['expense' => $model->public_id])); + return link_to("expenses/{$model->public_id}", trans('texts.view', ['expense' => $model->public_id])); } - ] + ]*/ ]; } -/* + protected function getDatatableActions($entityType) { - return [ + return [ [ - trans('texts.apply_expense'), + trans('texts.invoice_expense'), function ($model) { - return URL::to("espense/create/{$model->vendor_public_id}") . '?paymentTypeId=1'; + return URL::to("expense/invoice/{$model->public_id}") . '?client=1'; } - ] + ], + [ + trans('texts.view'), + function ($model) { + return URL::to("expenses/{$model->public_id}") ; + } + ], + ]; } - */ } \ No newline at end of file diff --git a/config/debugbar.php b/config/debugbar.php index a389dd17da36..57ec1876f5fc 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -89,7 +89,7 @@ return array( 'files' => false, // Show the included files 'config' => false, // Display config settings 'auth' => false, // Display Laravel authentication status - 'session' => false, // Display session data in a separate tab + 'session' => true, // Display session data in a separate tab ), /* diff --git a/database/migrations/2016_01_06_155001_create_expenses_table.php b/database/migrations/2016_01_06_155001_create_expenses_table.php index 95d538b3a69d..c5941a73195b 100644 --- a/database/migrations/2016_01_06_155001_create_expenses_table.php +++ b/database/migrations/2016_01_06_155001_create_expenses_table.php @@ -18,13 +18,12 @@ class CreateExpensesTable extends Migration { $table->increments('id'); $table->timestamps(); + $table->softDeletes(); $table->unsignedInteger('account_id')->index(); $table->unsignedInteger('vendor_id')->nullable(); $table->unsignedInteger('user_id'); - - $table->softDeletes(); - + $table->unsignedInteger('invoice_client_id')->nullable(); $table->boolean('is_deleted')->default(false); $table->decimal('amount', 13, 2); $table->decimal('amount_cur', 13, 2); @@ -36,9 +35,11 @@ class CreateExpensesTable extends Migration $table->boolean('is_invoiced')->default(false); $table->boolean('should_be_invoiced')->default(true); + // Relations $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');; + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + // Indexes $table->unsignedInteger('public_id')->index(); $table->unique( array('account_id','public_id') ); }); diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 120f46d5d6a1..8b277a16b03a 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1047,11 +1047,15 @@ return array( 'delete_expense' => 'Delete expense', 'view_expense_num' => 'Expense # :expense', 'updated_expense' => 'Expense updated', - + 'enter_expense' => 'Enter expense', + 'view' => 'View', + 'restore_expense' => 'Restore expense', + 'invoice_expense' => 'Invoice', // Payment terms 'num_days' => 'Number of days', 'create_payment_term' => 'Create payment term', 'edit_payment_terms' => 'Edit payment term', 'edit_payment_term' => 'Edit payment term', 'archive_payment_term' => 'Archive payment term', + ); diff --git a/resources/views/expenses/edit.blade.php b/resources/views/expenses/edit.blade.php index 031551717fc9..0e953849d153 100644 --- a/resources/views/expenses/edit.blade.php +++ b/resources/views/expenses/edit.blade.php @@ -33,13 +33,14 @@ {!! Former::textarea('private_notes') !!} {!! Former::textarea('public_notes') !!} {!! Former::checkbox('should_be_invoiced') !!} + {!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!}
    - {!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/credits'))->appendIcon(Icon::create('remove-circle')) !!} + {!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/dashboard'))->appendIcon(Icon::create('remove-circle')) !!} {!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
    @@ -49,6 +50,7 @@ var vendors = {!! $vendors !!}; + var clients = {!! $clients !!}; $(function() { @@ -76,7 +78,20 @@ $('.expense_date .input-group-addon').click(function() { toggleDatePicker('expense_date'); }); - }); + + + var $clientSelect = $('select#client'); + for (var i=0; i @stop \ No newline at end of file diff --git a/resources/views/list.blade.php b/resources/views/list.blade.php index 63efac9b5998..c479db50b993 100644 --- a/resources/views/list.blade.php +++ b/resources/views/list.blade.php @@ -11,6 +11,9 @@ @if ($entityType == ENTITY_TASK) {!! Button::primary(trans('texts.invoice'))->withAttributes(['class'=>'invoice', 'onclick' =>'submitForm("invoice")'])->appendIcon(Icon::create('check')) !!} @endif + @if ($entityType == ENTITY_EXPENSE) + {!! Button::primary(trans('texts.invoice'))->withAttributes(['class'=>'invoice', 'onclick' =>'submitForm("invoice")'])->appendIcon(Icon::create('check')) !!} + @endif {!! DropdownButton::normal(trans('texts.archive'))->withContents([ ['label' => trans('texts.archive_'.$entityType), 'url' => 'javascript:submitForm("archive")'], From d8cb1b436d60eef01daf5ef22c3e31740c548384 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Sat, 9 Jan 2016 09:08:24 +0100 Subject: [PATCH 13/20] Expense module --- app/Http/Controllers/ExpenseController.php | 3 ++- app/Ninja/Repositories/ExpenseRepository.php | 24 ++++++++++++++++++++ app/Services/ExpenseService.php | 6 +++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 2e59dbecd208..abfffc24f607 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -100,7 +100,8 @@ class ExpenseController extends BaseController 'title' => 'Edit Expense', 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), 'vendorPublicId' => $expense->vendor_id, - 'clients' => Client::scope()->with('contacts')->orderBy('name')->get() + 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), + 'clientPublicId' => $expense->invoice_client_id, ); $data = array_merge($data, self::getViewModel()); diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 12133947b510..80ce74ee3694 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -125,4 +125,28 @@ class ExpenseRepository extends BaseRepository return $expense; } + + public function bulk($ids, $action) + { + $expenses = Expense::withTrashed()->scope($ids)->get(); + + foreach ($expenses as $expense) { + if ($action == 'restore') { + $expense->restore(); + + $expense->is_deleted = false; + $expense->save(); + } else { + if ($action == 'delete') { + $expense->is_deleted = true; + $expense->save(); + } + + $expense->delete(); + } + } + + return count($tasks); + } + } diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index 46cbc2bb6e29..07a02445d655 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -104,6 +104,12 @@ class ExpenseService extends BaseService return URL::to("expenses/{$model->public_id}") ; } ], + [ + trans('texts.edit'), + function ($model) { + return URL::to("expenses/{$model->public_id}/edit") ; + } + ], ]; } From 3e9e28f06f8c6d0465e64da5487eef85e9fa5f95 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Sun, 10 Jan 2016 11:25:05 +0100 Subject: [PATCH 14/20] Expense module --- app/Http/Controllers/ExpenseController.php | 64 ++++++-- app/Http/Controllers/InvoiceController.php | 21 +-- app/Listeners/ExpenseListener.php | 10 +- app/Models/Invoice.php | 14 +- app/Ninja/Repositories/InvoiceRepository.php | 21 ++- app/Ninja/Transformers/InvoiceTransformer.php | 5 +- ...016_01_06_155001_create_expenses_table.php | 3 +- ...01_10_095302_add_invoices_has_expenses.php | 46 ++++++ resources/lang/en/texts.php | 11 +- resources/views/invoices/edit.blade.php | 144 ++++++++++-------- resources/views/invoices/knockout.blade.php | 88 +++++------ 11 files changed, 287 insertions(+), 140 deletions(-) create mode 100644 database/migrations/2016_01_10_095302_add_invoices_has_expenses.php diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index abfffc24f607..a6d82f17539c 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -111,7 +111,7 @@ class ExpenseController extends BaseController $data['proPlanPaid'] = $account['pro_plan_paid']; } } - + return View::make('expenses.edit', $data); } @@ -124,26 +124,70 @@ class ExpenseController extends BaseController 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'); - $count = $this->expenseService->bulk($ids, $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); @@ -152,7 +196,7 @@ class ExpenseController extends BaseController return Redirect::to('expenses'); } - + private static function getViewModel() { return [ @@ -172,7 +216,7 @@ class ExpenseController extends BaseController public function show($publicId) { $expense = Expense::withTrashed()->scope($publicId)->firstOrFail(); - + if($expense) { Utils::trackViewed($expense->getDisplayName(), 'expense'); } @@ -191,5 +235,5 @@ class ExpenseController extends BaseController ); return View::make('expenses.show', $data); - } + } } diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 63719526bd91..328facd601d1 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -119,11 +119,11 @@ class InvoiceController extends BaseController Session::put('invitation_key', $invitationKey); // track current invitation $account->loadLocalizationSettings($client); - + $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date); $invoice->is_pro = $account->isPro(); - + if ($invoice->invoice_design_id == CUSTOM_DESIGN) { $invoice->invoice_design->javascript = $account->custom_design; } else { @@ -204,7 +204,7 @@ class InvoiceController extends BaseController ->withTrashed() ->firstOrFail(); $entityType = $invoice->getEntityType(); - + $contactIds = DB::table('invitations') ->join('contacts', 'contacts.id', '=', 'invitations.contact_id') ->where('invitations.invoice_id', '=', $invoice->id) @@ -282,7 +282,7 @@ class InvoiceController extends BaseController 'actions' => $actions, 'lastSent' => $lastSent); $data = array_merge($data, self::getViewModel()); - + if ($clone) { $data['formIsChanged'] = true; } @@ -318,14 +318,14 @@ class InvoiceController extends BaseController $account = Auth::user()->account; $entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE; $clientId = null; - + if ($clientPublicId) { $clientId = Client::getPrivateId($clientPublicId); } - + $invoice = $account->createInvoice($entityType, $clientId); $invoice->public_id = 0; - + $data = [ 'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(), 'entityType' => $invoice->getEntityType(), @@ -335,7 +335,7 @@ class InvoiceController extends BaseController 'title' => trans('texts.new_invoice'), ]; $data = array_merge($data, self::getViewModel()); - + return View::make('invoices.edit', $data); } @@ -380,6 +380,7 @@ class InvoiceController extends BaseController 'recurringHelp' => $recurringHelp, 'invoiceLabels' => Auth::user()->account->getInvoiceLabels(), 'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null, + 'expenses' => Session::get('expenses') ? json_encode(Session::get('expenses')) : null, ]; } @@ -413,7 +414,7 @@ class InvoiceController extends BaseController if ($action == 'email') { return $this->emailInvoice($invoice, Input::get('pdfupload')); } - + return redirect()->to($invoice->getRoute()); } @@ -440,7 +441,7 @@ class InvoiceController extends BaseController } elseif ($action == 'email') { return $this->emailInvoice($invoice, Input::get('pdfupload')); } - + return redirect()->to($invoice->getRoute()); } diff --git a/app/Listeners/ExpenseListener.php b/app/Listeners/ExpenseListener.php index 0bccaffdb0ae..c8b0e7db5966 100644 --- a/app/Listeners/ExpenseListener.php +++ b/app/Listeners/ExpenseListener.php @@ -1,8 +1,9 @@ expenseRepo = $expenseRepo; } + + public function deletedInvoice(InvoiceWasDeleted $event) + { + // Release any tasks associated with the deleted invoice + Expense::where('invoice_id', '=', $event->invoice->id) + ->update(['invoice_id' => null]); + } } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index a80c7cfd2ad8..3cb18f4132b2 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -28,6 +28,7 @@ class Invoice extends EntityModel implements BalanceAffecting 'is_recurring' => 'boolean', 'has_tasks' => 'boolean', 'auto_bill' => 'boolean', + 'has_expenses' => 'boolean', ]; // used for custom invoice numbers @@ -82,7 +83,7 @@ class Invoice extends EntityModel implements BalanceAffecting public function getDisplayName() { - return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number; + return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number; } public function affectsBalance() @@ -136,7 +137,7 @@ class Invoice extends EntityModel implements BalanceAffecting return ($this->amount - $this->balance); } - + public function trashed() { if ($this->client && $this->client->trashed()) { @@ -207,7 +208,7 @@ class Invoice extends EntityModel implements BalanceAffecting $invitation->markSent($messageId); - // if the user marks it as sent rather than acually sending it + // if the user marks it as sent rather than acually sending it // then we won't track it in the activity log if (!$notify) { return; @@ -358,6 +359,7 @@ class Invoice extends EntityModel implements BalanceAffecting 'has_tasks', 'custom_text_value1', 'custom_text_value2', + 'has_expenses', ]); $this->client->setVisible([ @@ -451,7 +453,7 @@ class Invoice extends EntityModel implements BalanceAffecting // Fix for months with less than 31 days $transformerConfig = new \Recurr\Transformer\ArrayTransformerConfig(); $transformerConfig->enableLastDayOfMonthFix(); - + $transformer = new \Recurr\Transformer\ArrayTransformer(); $transformer->setConfig($transformerConfig); $dates = $transformer->transform($rule); @@ -477,7 +479,7 @@ class Invoice extends EntityModel implements BalanceAffecting if (count($schedule) < 2) { return null; } - + return $schedule[1]->getStart(); } @@ -539,7 +541,7 @@ class Invoice extends EntityModel implements BalanceAffecting if (!$nextSendDate = $this->getNextSendDate()) { return false; } - + return $this->account->getDateTime() >= $nextSendDate; } */ diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 11e0b31746d3..fa94085328b2 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -7,6 +7,7 @@ use App\Models\InvoiceItem; use App\Models\Invitation; use App\Models\Product; use App\Models\Task; +use App\Models\Expense; use App\Services\PaymentService; use App\Ninja\Repositories\BaseRepository; @@ -175,7 +176,7 @@ class InvoiceRepository extends BaseRepository $table->addColumn('balance', function ($model) { return $model->partial > 0 ? trans('texts.partial_remaining', [ - 'partial' => Utils::formatMoney($model->partial, $model->currency_id, $model->country_id), + 'partial' => Utils::formatMoney($model->partial, $model->currency_id, $model->country_id), 'balance' => Utils::formatMoney($model->balance, $model->currency_id, $model->country_id) ]) : Utils::formatMoney($model->balance, $model->currency_id, $model->country_id); @@ -204,6 +205,9 @@ class InvoiceRepository extends BaseRepository if (isset($data['has_tasks']) && filter_var($data['has_tasks'], FILTER_VALIDATE_BOOLEAN)) { $invoice->has_tasks = true; } + if (isset($data['has_expenses']) && filter_var($data['has_expenses'], FILTER_VALIDATE_BOOLEAN)) { + $invoice->has_expenses = true; + } } else { $invoice = Invoice::scope($publicId)->firstOrFail(); } @@ -265,7 +269,7 @@ class InvoiceRepository extends BaseRepository if (isset($data['po_number'])) { $invoice->po_number = trim($data['po_number']); } - + $invoice->invoice_design_id = isset($data['invoice_design_id']) ? $data['invoice_design_id'] : $account->invoice_design_id; if (isset($data['tax_name']) && isset($data['tax_rate']) && $data['tax_name']) { @@ -387,6 +391,14 @@ class InvoiceRepository extends BaseRepository $task->save(); } + if (isset($item['expense_public_id']) && $item['expense_public_id']) { + $expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail(); + $expense->invoice_id = $invoice->id; + $expense->invoice_client_id = $invoice->client_id; + $expense->is_invoiced = true; + $expense->save(); + } + if ($item['product_key']) { $productKey = trim($item['product_key']); if (\Auth::user()->account->update_products && ! strtotime($productKey)) { @@ -395,7 +407,10 @@ class InvoiceRepository extends BaseRepository $product = Product::createNew(); $product->product_key = trim($item['product_key']); } + $product->notes = $invoice->has_tasks ? '' : $item['notes']; + $product->notes = $invoice->has_expenses ? '' : $item['notes']; + $product->cost = $item['cost']; $product->save(); } @@ -639,7 +654,7 @@ class InvoiceRepository extends BaseRepository public function findNeedingReminding($account) { $dates = []; - + for ($i=1; $i<=3; $i++) { if ($date = $account->getReminderDate($i)) { $field = $account->{"field_reminder{$i}"} == REMINDER_FIELD_DUE_DATE ? 'due_date' : 'invoice_date'; diff --git a/app/Ninja/Transformers/InvoiceTransformer.php b/app/Ninja/Transformers/InvoiceTransformer.php index bc6ee6dbbd2f..a1bbb26e9ed1 100644 --- a/app/Ninja/Transformers/InvoiceTransformer.php +++ b/app/Ninja/Transformers/InvoiceTransformer.php @@ -23,7 +23,7 @@ class InvoiceTransformer extends EntityTransformer protected $defaultIncludes = [ 'invoice_items', ]; - + public function includeInvoiceItems(Invoice $invoice) { $transformer = new InvoiceItemTransformer($this->account, $this->serializer); @@ -70,6 +70,7 @@ class InvoiceTransformer extends EntityTransformer 'custom_value2' => $invoice->custom_value2, 'custom_taxes1' => (bool) $invoice->custom_taxes1, 'custom_taxes2' => (bool) $invoice->custom_taxes2, + 'has_expenses' => (bool) $invoice->has_expenses, ]; } -} \ No newline at end of file +} diff --git a/database/migrations/2016_01_06_155001_create_expenses_table.php b/database/migrations/2016_01_06_155001_create_expenses_table.php index c5941a73195b..6ddd62c3fe31 100644 --- a/database/migrations/2016_01_06_155001_create_expenses_table.php +++ b/database/migrations/2016_01_06_155001_create_expenses_table.php @@ -23,6 +23,7 @@ class CreateExpensesTable extends Migration $table->unsignedInteger('account_id')->index(); $table->unsignedInteger('vendor_id')->nullable(); $table->unsignedInteger('user_id'); + $table->unsignedInteger('invoice_id')->nullable(); $table->unsignedInteger('invoice_client_id')->nullable(); $table->boolean('is_deleted')->default(false); $table->decimal('amount', 13, 2); @@ -38,7 +39,7 @@ class CreateExpensesTable extends Migration // Relations $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - + // Indexes $table->unsignedInteger('public_id')->index(); $table->unique( array('account_id','public_id') ); diff --git a/database/migrations/2016_01_10_095302_add_invoices_has_expenses.php b/database/migrations/2016_01_10_095302_add_invoices_has_expenses.php new file mode 100644 index 000000000000..96e89d75e616 --- /dev/null +++ b/database/migrations/2016_01_10_095302_add_invoices_has_expenses.php @@ -0,0 +1,46 @@ +boolean('has_expenses')->default(false); + }); + + $invoices = DB::table('invoices') + ->join('expenses', 'expenses.invoice_id', '=', 'invoices.id') + ->selectRaw('DISTINCT invoices.id') + ->get(); + + foreach ($invoices as $invoice) { + DB::table('invoices') + ->where('id', $invoice->id) + ->update(['has_tasks' => true]); + } + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function(Blueprint $table) + { + $table->dropColumn('has_expenses'); + }); + } + +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 8b277a16b03a..f8c6842804fe 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -263,7 +263,7 @@ return array( 'deleted_vendor' => 'Successfully deleted vendor', 'deleted_vendors' => 'Successfully deleted :count vendors', - + // Emails 'confirmation_subject' => 'Invoice Ninja Account Confirmation', 'confirmation_header' => 'Account Confirmation', @@ -911,7 +911,7 @@ return array( 'default_invoice_footer' => 'Default Invoice Footer', 'quote_footer' => 'Quote Footer', 'free' => 'Free', - + 'quote_is_approved' => 'This quote is approved', 'apply_credit' => 'Apply Credit', 'system_settings' => 'System Settings', @@ -1026,7 +1026,7 @@ return array( 'archive_vendor' => 'Archive vendor', 'delete_vendor' => 'Delete vendor', 'view_vendor' => 'View vendor', - + // Expenses 'expense_amount' => 'Expense amount', 'expense_balance' => 'Expense balance', @@ -1051,11 +1051,14 @@ return array( 'view' => 'View', 'restore_expense' => 'Restore expense', 'invoice_expense' => 'Invoice', + 'expense_error_multiple_clients' =>'The expenses can\'t belong to different clients', + 'expense_error_invoiced' => 'Expense have already been invoiced', + 'expense_error_should_not_be_invoiced' => 'Expense maked not to be invoiced', + // Payment terms 'num_days' => 'Number of days', 'create_payment_term' => 'Create payment term', 'edit_payment_terms' => 'Edit payment term', 'edit_payment_term' => 'Edit payment term', 'archive_payment_term' => 'Archive payment term', - ); diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 6f8814a99c5f..9de4cbe3ed6a 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -28,7 +28,7 @@
  • {!! link_to(($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'), trans('texts.' . ($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'))) !!}
  • {{ $invoice->invoice_number }}
  • @endif - + @endif {!! Former::open($url) @@ -39,7 +39,7 @@ 'client' => 'required', 'invoice_number' => 'required', 'product_key' => 'max:255' - )) !!} + )) !!} @include('partials.autocomplete_fix') @@ -60,7 +60,7 @@ {{ trans('texts.edit_client') }} | {!! link_to('/clients/'.$invoice->client->public_id, trans('texts.view_client'), ['target' => '_blank']) !!} - +
    @endif @@ -69,7 +69,7 @@
    @@ -85,20 +85,20 @@    @if (Utils::isConfirmed()) - - @endif
    - +
    @@ -106,7 +106,7 @@ ->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('invoice_date') !!} {!! Former::text('due_date')->data_bind("datePicker: due_date, valueUpdate: 'afterkeydown'")->label(trans("texts.{$entityType}_due_date")) ->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('due_date') !!} - + {!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown'")->onchange('onPartialChange()') ->rel('tooltip')->data_toggle('tooltip')->data_placement('bottom')->title(trans('texts.partial_value')) !!}
    @@ -127,7 +127,7 @@ @if ($entityType == ENTITY_INVOICE)
    -
    +
    @if ($invoice->recurring_invoice) {!! trans('texts.created_by_invoice', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, trans('texts.recurring_invoice'))]) !!} @elseif ($invoice->id) @@ -151,7 +151,7 @@ {!! Former::text('invoice_number') ->label(trans("texts.{$entityType}_number_short")) ->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") !!} - + {!! Former::checkbox('auto_bill') ->label(trans('texts.auto_bill')) @@ -163,7 +163,7 @@ ->addGroupClass('discount-group')->type('number')->min('0')->step('any')->append( Former::select('is_amount_discount')->addOption(trans('texts.discount_percent'), '0') ->addOption(trans('texts.discount_amount'), '1')->data_bind("value: is_amount_discount")->raw() - ) !!} + ) !!} @if ($account->showCustomField('custom_invoice_text_label2', $invoice)) {!! Former::text('custom_text_value2')->label($account->custom_invoice_text_label2)->data_bind("value: custom_text_value2, valueUpdate: 'afterkeydown'") !!} @@ -200,16 +200,17 @@ !!} - + - - @@ -221,7 +222,7 @@
    - @@ -245,7 +246,7 @@
    {!! Former::textarea('public_notes')->data_bind("value: wrapped_notes, valueUpdate: 'afterkeydown'") - ->label(null)->style('resize: none; min-width: 450px;')->rows(3) !!} + ->label(null)->style('resize: none; min-width: 450px;')->rows(3) !!}
    {!! Former::textarea('terms')->data_bind("value:wrapped_terms, placeholder: terms_placeholder, valueUpdate: 'afterkeydown'") @@ -316,7 +317,7 @@ - + @if (!$account->hide_quantity) {{ trans('texts.tax') }} @endif @@ -369,7 +370,7 @@
    - +

     

    @@ -383,18 +384,19 @@ {!! Former::text('is_quote')->data_bind('value: is_quote') !!} {!! Former::text('has_tasks')->data_bind('value: has_tasks') !!} {!! Former::text('data')->data_bind('value: ko.mapping.toJSON(model)') !!} + {!! Former::text('has_expenses')->data_bind('value: has_expenses') !!} {!! Former::text('pdfupload') !!}
    @if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST) {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!} - @else + @else {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!} @endif - {!! Button::primary(trans('texts.download_pdf'))->withAttributes(array('onclick' => 'onDownloadClick()'))->appendIcon(Icon::create('download-alt')) !!} - + {!! Button::primary(trans('texts.download_pdf'))->withAttributes(array('onclick' => 'onDownloadClick()'))->appendIcon(Icon::create('download-alt')) !!} + @if ($invoice->isClientTrashed()) @elseif ($invoice->trashed()) @@ -448,7 +450,7 @@ {!! Former::text('client[vat_number]') ->label('vat_number') ->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") !!} - + {!! Former::text('client[website]') ->label('website') ->data_bind("value: website, valueUpdate: 'afterkeydown'") !!} @@ -457,7 +459,7 @@ ->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!} - @if (Auth::user()->isPro()) + @if (Auth::user()->isPro()) @if ($account->custom_client_label1) {!! Former::text('client[custom_value1]') ->label($account->custom_client_label1) @@ -503,11 +505,11 @@ {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown', attr: {name: 'client[contacts][' + \$index() + '][public_id]'}") !!} - {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', + {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', attr: {name: 'client[contacts][' + \$index() + '][first_name]'}") !!} {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown', attr: {name: 'client[contacts][' + \$index() + '][last_name]'}") !!} - {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', + {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', attr: {name: 'client[contacts][' + \$index() + '][email]', id:'email'+\$index()}") ->addClass('client-email') !!} {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', @@ -517,7 +519,7 @@
    {!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!} - + {!! link_to('#', trans('texts.add_contact').' +', array('data-bind'=>'click: $parent.addContact')) !!} @@ -540,7 +542,7 @@ ->placeholder($account->language ? $account->language->name : '') ->label(trans('texts.language_id')) ->data_bind('value: language_id') - ->fromQuery($languages, 'name', 'id') !!} + ->fromQuery($languages, 'name', 'id') !!} {!! Former::select('client[payment_terms]')->addOption('','')->data_bind('value: payment_terms') ->fromQuery($paymentTerms, 'name', 'num_days') ->label(trans('texts.payment_terms')) @@ -565,9 +567,9 @@   - +
    - +
    @@ -587,7 +589,7 @@ - + @@ -607,9 +609,9 @@ @include('invoices.knockout') \ No newline at end of file + From f90c7590784ed49af17b1f67cf2edbde8b127fb2 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Mon, 11 Jan 2016 20:20:42 +0100 Subject: [PATCH 15/20] Expense module --- app/Http/Controllers/DashboardController.php | 5 +++-- resources/lang/en/texts.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index cbf04ef79099..e56da3cb8017 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -13,6 +13,7 @@ class DashboardController extends BaseController { public function index() { + // total_income, billed_clients, invoice_sent and active_clients $select = DB::raw('COUNT(DISTINCT CASE WHEN invoices.id IS NOT NULL THEN clients.id ELSE null END) billed_clients, SUM(CASE WHEN invoices.invoice_status_id >= '.INVOICE_STATUS_SENT.' THEN 1 ELSE 0 END) invoices_sent, @@ -76,13 +77,13 @@ class DashboardController extends BaseController ->orderBy('created_at', 'desc') ->take(50) ->get(); - + $expenseactivities = ExpenseActivity::where('expense_activities.account_id', '=', Auth::user()->account_id) ->where('activity_type_id', '>', 0) ->orderBy('created_at', 'desc') ->take(50) ->get(); - + $pastDue = DB::table('invoices') ->leftJoin('clients', 'clients.id', '=', 'invoices.client_id') ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id') diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index f8c6842804fe..a49e703a0816 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -896,7 +896,7 @@ return array( 'activity_31' => ':user created :vendor', 'activity_32' => ':user created :vendor', 'activity_33' => ':user created :vendor', - 'activity_34' => ':user created :expense', + 'activity_34' => ':user created expense :expense', 'activity_35' => ':user created :vendor', 'activity_36' => ':user created :vendor', 'activity_37' => ':user created :vendor', From bcffedcbd3ffb443d7d1ea43a5f434e199783a64 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Mon, 11 Jan 2016 20:41:02 +0100 Subject: [PATCH 16/20] update from master --- app/Http/Controllers/InvoiceController.php | 22 +++++++++------------- resources/lang/en/texts.php | 11 +++++------ resources/views/invoices/edit.blade.php | 20 ++++++++------------ 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index cc33f585a262..485f2a3f2888 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -124,12 +124,8 @@ class InvoiceController extends BaseController $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date); $invoice->is_pro = $account->isPro(); -<<<<<<< HEAD - -======= $invoice->invoice_fonts = $account->getFontsData(); - ->>>>>>> cf24684adbce402f1c0e266672c4a2a5767dc754 + if ($invoice->invoice_design_id == CUSTOM_DESIGN) { $invoice->invoice_design->javascript = $account->custom_design; } else { @@ -364,7 +360,7 @@ class InvoiceController extends BaseController $recurringHelp .= $line; } } - + $recurringDueDateHelp = ''; foreach (preg_split("/((\r?\n)|(\r\n?))/", trans('texts.recurring_due_date_help')) as $line) { $parts = explode("=>", $line); @@ -380,20 +376,20 @@ class InvoiceController extends BaseController $recurringDueDates = array( trans('texts.use_client_terms') => array('value' => '', 'class' => 'monthly weekly'), ); - + $ends = array('th','st','nd','rd','th','th','th','th','th','th'); for($i = 1; $i < 31; $i++){ if ($i >= 11 && $i <= 13) $ordinal = $i. 'th'; else $ordinal = $i . $ends[$i % 10]; - + $dayStr = str_pad($i, 2, '0', STR_PAD_LEFT); $str = trans('texts.day_of_month', array('ordinal'=>$ordinal)); - + $recurringDueDates[$str] = array('value' => "1998-01-$dayStr", 'data-num' => $i, 'class' => 'monthly'); } $recurringDueDates[trans('texts.last_day_of_month')] = array('value' => "1998-01-31", 'data-num' => 31, 'class' => 'monthly'); - - + + $daysOfWeek = array( trans('texts.sunday'), trans('texts.monday'), @@ -406,13 +402,13 @@ class InvoiceController extends BaseController foreach(array('1st','2nd','3rd','4th') as $i=>$ordinal){ foreach($daysOfWeek as $j=>$dayOfWeek){ $str = trans('texts.day_of_week_after', array('ordinal' => $ordinal, 'day' => $dayOfWeek)); - + $day = $i * 7 + $j + 1; $dayStr = str_pad($day, 2, '0', STR_PAD_LEFT); $recurringDueDates[$str] = array('value' => "1998-02-$dayStr", 'data-num' => $day, 'class' => 'weekly'); } } - + return [ 'data' => Input::old('data'), 'account' => Auth::user()->account->load('country'), diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 9062df55e8cc..d07ba551d29d 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -992,7 +992,7 @@ return array( 'custom_account_fields_helps' => 'Add a label and value to the company details section of the PDF.', 'custom_invoice_fields_helps' => 'Add a text input to the invoice create/edit page and display the label and value on the PDF.', 'custom_invoice_charges_helps' => 'Add a text input to the invoice create/edit page and include the charge in the invoice subtotals.', - + 'token_expired' => 'Validation token was expired. Please try again.', 'invoice_link' => 'Invoice Link', 'button_confirmation_message' => 'Click to confirm your email address.', @@ -1012,7 +1012,7 @@ return array( 'white_label_custom_css' => ':link for $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.', 'white_label_purchase_link' => 'Purchase a white label license', -<<<<<<< HEAD + // Expense / vendor 'expense' => 'Expense', @@ -1061,9 +1061,8 @@ return array( 'edit_payment_terms' => 'Edit payment term', 'edit_payment_term' => 'Edit payment term', 'archive_payment_term' => 'Archive payment term', -); -======= - + + // recurring due dates 'recurring_due_dates' => 'Recurring Invoice Due Dates', 'recurring_due_date_help' => '

    Automatically sets a due date for the invoice.

    @@ -1106,4 +1105,4 @@ return array( 'payment_message_button' => 'Thank you for your payment of :amount.', ); ->>>>>>> cf24684adbce402f1c0e266672c4a2a5767dc754 + diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index c778743d9dad..46cc54e80efe 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -175,10 +175,10 @@ @if ($account->showCustomField('custom_invoice_text_label2', $invoice)) {!! Former::text('custom_text_value2')->label($account->custom_invoice_text_label2)->data_bind("value: custom_text_value2, valueUpdate: 'afterkeydown'") !!} @endif - + @if ($entityType == ENTITY_INVOICE)
    -
    +
    @if ($invoice->recurring_invoice) {!! trans('texts.created_by_invoice', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, trans('texts.recurring_invoice'))]) !!} @elseif ($invoice->id) @@ -628,7 +628,7 @@
    - + @@ -673,7 +673,7 @@ var $clientSelect = $('select#client'); var invoiceDesigns = {!! $invoiceDesigns !!}; var invoiceFonts = {!! $invoiceFonts !!}; - + $(function() { // create client dictionary @@ -892,20 +892,17 @@ @endif applyComboboxListeners(); -<<<<<<< HEAD }); -======= - }); - + function onFrequencyChange(){ var currentName = $('#frequency_id').find('option:selected').text() var currentDueDateNumber = $('#recurring_due_date').find('option:selected').attr('data-num'); var optionClass = currentName && currentName.toLowerCase().indexOf('week') > -1 ? 'weekly' : 'monthly'; var replacementOption = $('#recurring_due_date option[data-num=' + currentDueDateNumber + '].' + optionClass); - + $('#recurring_due_date option').hide(); $('#recurring_due_date option.' + optionClass).show(); - + // Switch to an equivalent option if(replacementOption.length){ replacementOption.attr('selected','selected'); @@ -914,7 +911,6 @@ $('#recurring_due_date').val(''); } } ->>>>>>> cf24684adbce402f1c0e266672c4a2a5767dc754 function applyComboboxListeners() { var selectorStr = '.invoice-table input, .invoice-table textarea'; From 172c1a05480bd197794bc40c8c3e06b815355bab Mon Sep 17 00:00:00 2001 From: steenrabol Date: Mon, 11 Jan 2016 20:56:12 +0100 Subject: [PATCH 17/20] update --- composer.lock | 36 ++++++++++++------------- resources/views/invoices/edit.blade.php | 3 --- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/composer.lock b/composer.lock index a5fb2a6d22aa..c70a49e7ffe4 100644 --- a/composer.lock +++ b/composer.lock @@ -1481,16 +1481,16 @@ }, { "name": "doctrine/dbal", - "version": "v2.5.3", + "version": "v2.5.4", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "2fbcea96eae34a53183377cdbb0b9bec33974648" + "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/2fbcea96eae34a53183377cdbb0b9bec33974648", - "reference": "2fbcea96eae34a53183377cdbb0b9bec33974648", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/abbdfd1cff43a7b99d027af3be709bc8fc7d4769", + "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769", "shasum": "" }, "require": { @@ -1548,7 +1548,7 @@ "persistence", "queryobject" ], - "time": "2015-12-25 16:28:24" + "time": "2016-01-05 22:11:12" }, { "name": "doctrine/inflector", @@ -2254,12 +2254,12 @@ "source": { "type": "git", "url": "https://github.com/Intervention/image.git", - "reference": "9f29360b8ab94585cb9e80cf9045abd5b85feb89" + "reference": "86dfe2f2a95aa9eed58faf1cc1dae6534a6ce931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/image/zipball/9f29360b8ab94585cb9e80cf9045abd5b85feb89", - "reference": "9f29360b8ab94585cb9e80cf9045abd5b85feb89", + "url": "https://api.github.com/repos/Intervention/image/zipball/86dfe2f2a95aa9eed58faf1cc1dae6534a6ce931", + "reference": "86dfe2f2a95aa9eed58faf1cc1dae6534a6ce931", "shasum": "" }, "require": { @@ -2308,7 +2308,7 @@ "thumbnail", "watermark" ], - "time": "2016-01-02 19:15:13" + "time": "2016-01-10 11:20:02" }, { "name": "ircmaxell/password-compat", @@ -3463,12 +3463,12 @@ "source": { "type": "git", "url": "https://github.com/meebio/omnipay-secure-trading.git", - "reference": "42f97ee5ad1d28605550d816fc1893919e19e502" + "reference": "992224a3c8dd834ee18f6f253a77ecb4c87c1c1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/meebio/omnipay-secure-trading/zipball/42f97ee5ad1d28605550d816fc1893919e19e502", - "reference": "42f97ee5ad1d28605550d816fc1893919e19e502", + "url": "https://api.github.com/repos/meebio/omnipay-secure-trading/zipball/992224a3c8dd834ee18f6f253a77ecb4c87c1c1a", + "reference": "992224a3c8dd834ee18f6f253a77ecb4c87c1c1a", "shasum": "" }, "require": { @@ -3513,7 +3513,7 @@ "secure trading", "securetrading" ], - "time": "2015-12-01 10:03:20" + "time": "2016-01-05 09:26:36" }, { "name": "mfauveau/omnipay-pacnet", @@ -7030,16 +7030,16 @@ }, { "name": "true/punycode", - "version": "v2.0.1", + "version": "v2.0.2", "source": { "type": "git", "url": "https://github.com/true/php-punycode.git", - "reference": "b672918d992b84f8016bbe353a42516928393c63" + "reference": "74fa01d4de396c40e239794123b3874cb594a30c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/true/php-punycode/zipball/b672918d992b84f8016bbe353a42516928393c63", - "reference": "b672918d992b84f8016bbe353a42516928393c63", + "url": "https://api.github.com/repos/true/php-punycode/zipball/74fa01d4de396c40e239794123b3874cb594a30c", + "reference": "74fa01d4de396c40e239794123b3874cb594a30c", "shasum": "" }, "require": { @@ -7072,7 +7072,7 @@ "idna", "punycode" ], - "time": "2015-09-01 14:53:31" + "time": "2016-01-07 17:12:58" }, { "name": "twbs/bootstrap", diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 46cc54e80efe..7f404a4bedd1 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -128,7 +128,6 @@ @if ($account->showCustomField('custom_invoice_text_label1', $invoice)) {!! Former::text('custom_text_value1')->label($account->custom_invoice_text_label1)->data_bind("value: custom_text_value1, valueUpdate: 'afterkeydown'") !!} @endif -<<<<<<< HEAD @if ($entityType == ENTITY_INVOICE)
    @@ -149,8 +148,6 @@
    @endif -======= ->>>>>>> cf24684adbce402f1c0e266672c4a2a5767dc754
    From f550e3734f18ee764a6bc39353b767ab5c6fbf70 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Fri, 15 Jan 2016 11:31:12 +0100 Subject: [PATCH 18/20] bug fix to expense module - was not able to add a contact to a new vendor --- app/Http/Controllers/VendorController.php | 11 +++--- app/Http/routes.php | 26 +++++++------- app/Models/Expense.php | 18 ++++++---- app/Models/Vendor.php | 18 +++++++--- resources/views/expenses/show.blade.php | 8 ++--- resources/views/vendors/edit.blade.php | 42 +++++++++++------------ resources/views/vendors/show.blade.php | 16 ++++----- 7 files changed, 77 insertions(+), 62 deletions(-) diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 7fac33b1f840..b42eef837efb 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -75,9 +75,9 @@ class VendorController extends BaseController public function store(CreateVendorRequest $request) { $vendor = $this->vendorService->save($request->input()); - + Session::flash('message', trans('texts.created_vendor')); - + return redirect()->to($vendor->getRoute()); } @@ -167,7 +167,8 @@ class VendorController extends BaseController 'data' => Input::old('data'), 'account' => Auth::user()->account, 'sizes' => Cache::get('sizes'), - 'paymentTerms' => Cache::get('paymentTerms'), + //'paymentTerms' => Cache::get('paymentTerms'), + 'paymentTerms' => PaymentTerm::get(), 'industries' => Cache::get('industries'), 'currencies' => Cache::get('currencies'), 'languages' => Cache::get('languages'), @@ -186,9 +187,9 @@ class VendorController extends BaseController public function update(UpdateVendorRequest $request) { $vendor = $this->vendorService->save($request->input()); - + Session::flash('message', trans('texts.updated_vendor')); - + return redirect()->to($vendor->getRoute()); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 2e1262fb6e27..61a180c2746e 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -87,7 +87,7 @@ Route::group(['middleware' => 'auth'], function() { Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible'); Route::get('hide_message', 'HomeController@hideMessage'); Route::get('force_inline_pdf', 'UserController@forcePDFJS'); - + Route::get('api/users', array('as'=>'api.users', 'uses'=>'UserController@getDatatable')); Route::resource('users', 'UserController'); Route::post('users/bulk', 'UserController@bulk'); @@ -118,18 +118,18 @@ Route::group(['middleware' => 'auth'], function() { Route::post('settings/cancel_account', 'AccountController@cancelAccount'); Route::get('settings/{section?}', 'AccountController@showSection'); Route::post('settings/{section?}', 'AccountController@doSection'); - + // Payment term Route::get('api/payment_terms', array('as'=>'api.payment_terms', 'uses'=>'PaymentTermController@getDatatable')); Route::resource('payment_terms', 'PaymentTermController'); Route::post('payment_terms/bulk', 'PaymentTermController@bulk'); - + Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData')); Route::post('user/setTheme', 'UserController@setTheme'); Route::post('remove_logo', 'AccountController@removeLogo'); Route::post('account/go_pro', 'AccountController@enableProPlan'); - + Route::post('/export', 'ExportController@doExport'); Route::post('/import', 'ImportController@doImport'); Route::post('/import_csv', 'ImportController@doImportCSV'); @@ -149,10 +149,10 @@ Route::group(['middleware' => 'auth'], function() { Route::post('tasks/bulk', 'TaskController@bulk'); Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'InvoiceController@getRecurringDatatable')); - + Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory'); Route::get('quotes/quote_history/{invoice_id}', 'InvoiceController@invoiceHistory'); - + Route::resource('invoices', 'InvoiceController'); Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable')); Route::get('invoices/create/{client_id?}', 'InvoiceController@create'); @@ -188,7 +188,7 @@ Route::group(['middleware' => 'auth'], function() { get('/resend_confirmation', 'AccountController@resendConfirmation'); post('/update_setup', 'AppController@updateSetup'); - + // vendor Route::resource('vendors', 'VendorController'); Route::get('api/vendor', array('as'=>'api.vendors', 'uses'=>'VendorController@getDatatable')); @@ -200,8 +200,8 @@ Route::group(['middleware' => 'auth'], function() { 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/expenseactivities/{vendor_id?}', array('as'=>'api.expenseactivities', 'uses'=>'ExpenseActivityController@getDatatable')); - + Route::get('api/expenseactivities/{expense_id?}', array('as'=>'api.expenseactivities', 'uses'=>'ExpenseActivityController@getDatatable')); + }); // Route groups for API @@ -287,11 +287,11 @@ if (!defined('CONTACT_EMAIL')) { define('ENTITY_EXPENSE', 'expense'); define('ENTITY_PAYMENT_TERM','payment_term'); define('ENTITY_EXPENSE_ACTIVITY','expense_activity'); - + define('PERSON_CONTACT', 'contact'); define('PERSON_USER', 'user'); define('PERSON_VENDOR_CONTACT','vendorcontact'); - + define('BASIC_SETTINGS', 'basic_settings'); define('ADVANCED_SETTINGS', 'advanced_settings'); @@ -369,7 +369,7 @@ if (!defined('CONTACT_EMAIL')) { define('ACTIVITY_TYPE_ARCHIVE_EXPENSE', 35); define('ACTIVITY_TYPE_DELETE_EXPENSE', 36); define('ACTIVITY_TYPE_RESTORE_EXPENSE', 37); - + define('DEFAULT_INVOICE_NUMBER', '0001'); define('RECENTLY_VIEWED_LIMIT', 8); define('LOGGED_ERROR_LIMIT', 100); @@ -404,7 +404,7 @@ if (!defined('CONTACT_EMAIL')) { define('MAX_NUM_VENDORS', 100); define('MAX_NUM_VENDORS_PRO', 20000); define('MAX_NUM_VENDORS_LEGACY', 500); - + define('INVOICE_STATUS_DRAFT', 1); define('INVOICE_STATUS_SENT', 2); define('INVOICE_STATUS_VIEWED', 3); diff --git a/app/Models/Expense.php b/app/Models/Expense.php index a666f558cf0b..daf652d4e415 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -4,13 +4,14 @@ use Laracasts\Presenter\PresentableTrait; use Illuminate\Database\Eloquent\SoftDeletes; use App\Events\ExpenseWasCreated; use App\Events\ExpenseWasUpdated; +use App\Events\ExpenseWasDeleted; class Expense extends EntityModel { // Expenses use SoftDeletes; use PresentableTrait; - + protected $dates = ['deleted_at']; protected $presenter = 'App\Ninja\Presenters\ExpensePresenter'; @@ -20,7 +21,7 @@ class Expense extends EntityModel 'exchange_rate', 'private_notes', 'public_notes', - ]; + ]; public function account() { return $this->belongsTo('App\Models\Account'); @@ -40,10 +41,10 @@ class Expense extends EntityModel { if($this->expense_number) return $this->expense_number; - + return $this->public_id; } - + public function getDisplayName() { return $this->getName(); @@ -53,7 +54,7 @@ class Expense extends EntityModel { return "/expenses/{$this->public_id}"; } - + public function getEntityType() { return ENTITY_EXPENSE; @@ -91,5 +92,10 @@ Expense::updated(function ($expense) { event(new ExpenseWasUpdated($expense)); }); +Expense::deleting(function ($expense) { + $expense->setNullValues(); +}); - +Expense::deleted(function ($expense) { + event(new ExpenseWasDeleted($expense)); +}); diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index c228c636011b..0fc8aff3666c 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -154,7 +154,7 @@ class Vendor extends EntityModel $this->balance = $this->balance + $balanceAdjustment; $this->paid_to_date = $this->paid_to_date + $paidToDateAdjustment; - + $this->save(); } @@ -167,18 +167,18 @@ class Vendor extends EntityModel { return 0; } - + public function getName() { return $this->name; } - + public function getDisplayName() { if ($this->name) { return $this->name; } - + if ( ! count($this->contacts)) { return ''; } @@ -236,7 +236,7 @@ class Vendor extends EntityModel } $accountGateway = $this->account->getGatewayConfig(GATEWAY_STRIPE); - + if (!$accountGateway) { return false; } @@ -282,3 +282,11 @@ Vendor::updated(function ($vendor) { event(new VendorWasUpdated($vendor)); }); + +Vendor::deleting(function ($vendor) { + $vendor->setNullValues(); +}); + +Vendor::deleted(function ($vendor) { + event(new VendorWasDeleted($vendor)); +}); diff --git a/resources/views/expenses/show.blade.php b/resources/views/expenses/show.blade.php index f8d83d97350b..dd0c613b7a3c 100644 --- a/resources/views/expenses/show.blade.php +++ b/resources/views/expenses/show.blade.php @@ -74,12 +74,11 @@
    {!! Datatable::table() ->addColumn( - trans('texts.date'), + trans('texts.expense_date'), trans('texts.message'), - trans('texts.balance'), - trans('texts.adjustment')) + trans('texts.amount'), + trans('texts.public_notes')) ->setUrl(url('api/expenseactivities/'. $expense->public_id)) - ->setCustomValues('entityType', 'activity') ->setOptions('sPaginationType', 'bootstrap') ->setOptions('bFilter', false) ->setOptions('aaSorting', [['0', 'desc']]) @@ -98,6 +97,7 @@ $('.primaryDropDown:not(.dropdown-toggle)').click(function() { window.location = '{{ URL::to('expenses/create/' . $expense->public_id ) }}'; }); + }); function onArchiveClick() { diff --git a/resources/views/vendors/edit.blade.php b/resources/views/vendors/edit.blade.php index aaf25c1c0ee5..ef37f3dec67b 100644 --- a/resources/views/vendors/edit.blade.php +++ b/resources/views/vendors/edit.blade.php @@ -7,8 +7,8 @@ @section('content') -@if ($errors->first('vendor_contacts')) -
    {{ trans($errors->first('vendor_contacts')) }}
    +@if ($errors->first('vendorcontacts')) +
    {{ trans($errors->first('vendorcontacts')) }}
    @endif
    @@ -19,7 +19,7 @@ ['email' => 'email'] )->addClass('col-md-12 warn-on-exit') ->method($method) !!} - + @include('partials.autocomplete_fix') @if ($vendor) @@ -36,13 +36,13 @@

    {!! trans('texts.organization') !!}

    - + {!! Former::text('name')->data_bind("attr { placeholder: placeholderName }") !!} {!! Former::text('id_number') !!} {!! Former::text('vat_number') !!} {!! Former::text('website') !!} {!! Former::text('work_phone') !!} - + @if (Auth::user()->isPro()) @if ($customLabel1) {!! Former::text('custom_value1')->label($customLabel1) !!} @@ -59,7 +59,7 @@

    {!! trans('texts.address') !!}

    - + {!! Former::text('address1') !!} {!! Former::text('address2') !!} {!! Former::text('city') !!} @@ -84,21 +84,21 @@ beforeRemove: hideContact, afterAdd: showContact }'> {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown', - attr: {name: 'vendor_contacts[' + \$index() + '][public_id]'}") !!} - {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', - attr: {name: 'vendor_contacts[' + \$index() + '][first_name]'}") !!} + attr: {name: 'vendorcontacts[' + \$index() + '][public_id]'}") !!} + {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', + attr: {name: 'vendorcontacts[' + \$index() + '][first_name]'}") !!} {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown', - attr: {name: 'vendor_contacts[' + \$index() + '][last_name]'}") !!} - {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', - attr: {name: 'vendor_contacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} + attr: {name: 'vendorcontacts[' + \$index() + '][last_name]'}") !!} + {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', + attr: {name: 'vendorcontacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', - attr: {name: 'vendor_contacts[' + \$index() + '][phone]'}") !!} + attr: {name: 'vendorcontacts[' + \$index() + '][phone]'}") !!}
    {!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!} - + {!! link_to('#', trans('texts.add_contact').' +', array('onclick'=>'return addContact()')) !!} @@ -114,7 +114,7 @@

    {!! trans('texts.additional_info') !!}

    - + {!! Former::select('currency_id')->addOption('','') ->placeholder($account->currency ? $account->currency->name : '') ->fromQuery($currencies, 'name', 'id') !!} @@ -175,10 +175,10 @@ function VendorModel(data) { var self = this; - self.vendor_contacts = ko.observableArray(); + self.vendorcontacts = ko.observableArray(); self.mapping = { - 'vendor_contacts': { + 'vendorcontacts': { create: function(options) { return new VendorContactModel(options.data); } @@ -188,18 +188,18 @@ if (data) { ko.mapping.fromJS(data, self.mapping, this); } else { - self.vendor_contacts.push(new VendorContactModel()); + self.vendorcontacts.push(new VendorContactModel()); } self.placeholderName = ko.computed(function() { - if (self.vendor_contacts().length == 0) return ''; - var contact = self.vendor_contacts()[0]; + if (self.vendorcontacts().length == 0) return ''; + var contact = self.vendorcontacts()[0]; if (contact.first_name() || contact.last_name()) { return contact.first_name() + ' ' + contact.last_name(); } else { return contact.email(); } - }); + }); } @if ($data) diff --git a/resources/views/vendors/show.blade.php b/resources/views/vendors/show.blade.php index 7fea8b976ec2..7be091d7e9e0 100644 --- a/resources/views/vendors/show.blade.php +++ b/resources/views/vendors/show.blade.php @@ -100,13 +100,13 @@ @if ($vendor->private_notes)

    {{ $vendor->private_notes }}

    @endif - + @if ($vendor->vendor_industry) {{ $vendor->vendor_industry->name }}
    @endif @if ($vendor->vendor_size) {{ $vendor->vendor_size->name }}
    - @endif + @endif @if ($vendor->website)

    {!! Utils::formatWebsite($vendor->website) !!}

    @@ -130,7 +130,7 @@ @endif @if ($contact->phone) {{ $contact->phone }}
    - @endif + @endif @endforeach
    @@ -194,7 +194,7 @@ trans('texts.credit_balance'), trans('texts.credit_date'), trans('texts.private_notes')) - ->setUrl(url('api/credits/' . $vendor->public_id)) + ->setUrl(url('api/expenses/' . $vendor->public_id)) ->setCustomValues('entityType', 'credits') ->setOptions('sPaginationType', 'bootstrap') ->setOptions('bFilter', false) @@ -266,14 +266,14 @@ var map = new google.maps.Map(mapCanvas, mapOptions) var address = "{{ "{$vendor->address1} {$vendor->address2} {$vendor->city} {$vendor->state} {$vendor->postal_code} " . ($vendor->country ? $vendor->country->name : '') }}"; - + geocoder = new google.maps.Geocoder(); geocoder.geocode( { 'address': address}, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { if (status != google.maps.GeocoderStatus.ZERO_RESULTS) { var result = results[0]; map.setCenter(result.geometry.location); - + var infowindow = new google.maps.InfoWindow( { content: ''+result.formatted_address+'', size: new google.maps.Size(150, 50) @@ -281,9 +281,9 @@ var marker = new google.maps.Marker({ position: result.geometry.location, - map: map, + map: map, title:address, - }); + }); google.maps.event.addListener(marker, 'click', function() { infowindow.open(map, marker); }); From e7a658aaf6b4505ee6fc1548da44ed49a77e22bc Mon Sep 17 00:00:00 2001 From: steenrabol Date: Tue, 19 Jan 2016 14:01:19 +0100 Subject: [PATCH 19/20] Expenses bug fix --- .gitignore | 2 + app/Http/Controllers/AccountController.php | 24 +- app/Http/Controllers/VendorController.php | 21 +- app/Libraries/Utils.php | 26 +- app/Models/Vendor.php | 62 +---- .../ExpenseActivityRepository.php | 11 +- app/Ninja/Repositories/VendorRepository.php | 19 +- app/Services/VendorActivityService.php | 9 +- app/Services/VendorService.php | 12 +- composer.json | 2 +- composer.lock | 251 +++++++++++------- config/app.php | 2 + ...2016_01_04_175228_create_vendors_table.php | 8 +- invoiceninja.komodoproject | 2 +- resources/views/invoices/edit.blade.php | 2 + resources/views/vendors/show.blade.php | 48 +--- 16 files changed, 233 insertions(+), 268 deletions(-) diff --git a/.gitignore b/.gitignore index b643269b604a..5a86589d7a21 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ tests/_bootstrap.php # composer stuff /c3.php + +_ide_helper.php \ No newline at end of file diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 126eadc5a717..cc51a06de32c 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -326,7 +326,7 @@ class AccountController extends BaseController return View::make('accounts.payment_terms', $data); } - + private function showInvoiceDesign($section) { $account = Auth::user()->account->load('country'); @@ -336,13 +336,15 @@ class AccountController extends BaseController $invoiceItem = new stdClass(); $client->name = 'Sample Client'; - $client->address1 = ''; - $client->city = ''; - $client->state = ''; - $client->postal_code = ''; - $client->work_phone = ''; - $client->work_email = ''; - + $client->address1 = 'address 1'; + $client->city = ' city'; + $client->state = 'state'; + $client->postal_code = 'postal code'; + $client->work_phone = 'work phone'; + $client->work_email = 'work email'; + $client->id_number = 'cleint id'; + $client->vat_number = 'vat number'; + $invoice->invoice_number = '0000'; $invoice->invoice_date = Utils::fromSqlDate(date('Y-m-d')); $invoice->account = json_decode($account->toJson()); @@ -361,7 +363,7 @@ class AccountController extends BaseController $invoice->client = $client; $invoice->invoice_items = [$invoiceItem]; - + $data['account'] = $account; $data['invoice'] = $invoice; $data['invoiceLabels'] = json_decode($account->invoice_labels) ?: []; @@ -369,7 +371,7 @@ class AccountController extends BaseController $data['invoiceDesigns'] = InvoiceDesign::getDesigns(); $data['invoiceFonts'] = Cache::get('fonts'); $data['section'] = $section; - + $design = false; foreach ($data['invoiceDesigns'] as $item) { if ($item->id == $account->invoice_design_id) { @@ -711,7 +713,7 @@ class AccountController extends BaseController $account->primary_color = Input::get('primary_color'); $account->secondary_color = Input::get('secondary_color'); $account->invoice_design_id = Input::get('invoice_design_id'); - + if (Input::has('font_size')) { $account->font_size = intval(Input::get('font_size')); } diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index b42eef837efb..c7eced17f500 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -37,6 +37,8 @@ class VendorController extends BaseController $this->vendorRepo = $vendorRepo; $this->vendorService = $vendorService; + + } /** @@ -93,19 +95,18 @@ class VendorController extends BaseController Utils::trackViewed($vendor->getDisplayName(), 'vendor'); $actionLinks = [ - ['label' => trans('texts.new_vendor'), 'url' => '/vendors/create/'.$vendor->public_id] + ['label' => trans('texts.new_vendor'), 'url' => '/vendors/create/' . $vendor->public_id] ]; $data = array( - 'actionLinks' => $actionLinks, - 'showBreadcrumbs' => false, - 'vendor' => $vendor, - 'credit' => $vendor->getTotalCredit(), - 'title' => trans('texts.view_vendor'), - 'hasRecurringInvoices' => false, - 'hasQuotes' => false, - 'hasTasks' => false, - 'gatewayLink' => $vendor->getGatewayLink(), + 'actionLinks' => $actionLinks, + 'showBreadcrumbs' => false, + 'vendor' => $vendor, + 'totalexpense' => $vendor->getTotalExpense(), + 'title' => trans('texts.view_vendor'), + 'hasRecurringInvoices' => false, + 'hasQuotes' => false, + 'hasTasks' => false, ); return View::make('vendors.show', $data); diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 6005cc3d5375..7efb2b748447 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -137,7 +137,7 @@ class Utils $history = Session::get(RECENTLY_VIEWED); $last = $history[0]; $penultimate = count($history) > 1 ? $history[1] : $last; - + return Request::url() == $last->url ? $penultimate->url : $last->url; } @@ -249,7 +249,7 @@ class Utils $data = Cache::get($type)->filter(function($item) use ($id) { return $item->id == $id; }); - + return $data->first(); } @@ -344,7 +344,7 @@ class Utils if (!$date) { return false; } - + $dateTime = new DateTime($date); $timestamp = $dateTime->getTimestamp(); $format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT); @@ -468,7 +468,7 @@ class Utils } array_unshift($data, $object); - + if (isset($counts[Auth::user()->account_id]) && $counts[Auth::user()->account_id] > RECENTLY_VIEWED_LIMIT) { array_pop($data); } @@ -579,9 +579,15 @@ class Utils public static function getVendorDisplayName($model) { - return $model->getDisplayName(); + if(is_null($model)) + return ''; + + if($model->vendor_name) + return $model->vendor_name; + + return 'No vendor name'; } - + public static function getPersonDisplayName($firstName, $lastName, $email) { if ($firstName || $lastName) { @@ -673,7 +679,7 @@ class Utils if ($publicId) { $data['id'] = $publicId; } - + return $data; } @@ -719,7 +725,7 @@ class Utils $str .= 'ENTITY_DELETED '; } } - + if ($model->deleted_at && $model->deleted_at != '0000-00-00') { $str .= 'ENTITY_ARCHIVED '; } @@ -739,7 +745,7 @@ class Utils fwrite($output, "\n"); } - + public static function getFirst($values) { if (is_array($values)) { @@ -904,7 +910,7 @@ class Utils if (!preg_match("~^(?:f|ht)tps?://~i", $url)) { $url = "http://" . $url; } - + return $url; } } diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 0fc8aff3666c..481967232ff6 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -137,7 +137,6 @@ class Vendor extends EntityModel $contact = VendorContact::scope($publicId)->firstOrFail(); } else { $contact = VendorContact::createNew(); - //$contact->send_invoice = true; } $contact->fill($data); @@ -146,28 +145,11 @@ class Vendor extends EntityModel return $this->vendorContacts()->save($contact); } - public function updateBalances($balanceAdjustment, $paidToDateAdjustment) - { - if ($balanceAdjustment === 0 && $paidToDateAdjustment === 0) { - return; - } - - $this->balance = $this->balance + $balanceAdjustment; - $this->paid_to_date = $this->paid_to_date + $paidToDateAdjustment; - - $this->save(); - } - public function getRoute() { return "/vendors/{$this->public_id}"; } - public function getTotalCredit() - { - return 0; - } - public function getName() { return $this->name; @@ -175,16 +157,7 @@ class Vendor extends EntityModel public function getDisplayName() { - if ($this->name) { - return $this->name; - } - - if ( ! count($this->contacts)) { - return ''; - } - - $contact = $this->contacts[0]; - return $contact->getDisplayName(); + return $this->getName(); } public function getCityState() @@ -227,31 +200,6 @@ class Vendor extends EntityModel } } - public function getGatewayToken() - { - $this->account->load('account_gateways'); - - if (!count($this->account->account_gateways)) { - return false; - } - - $accountGateway = $this->account->getGatewayConfig(GATEWAY_STRIPE); - - if (!$accountGateway) { - return false; - } - - $token = AccountGatewayToken::where('vendor_id', '=', $this->id)->where('account_gateway_id', '=', $accountGateway->id)->first(); - - return $token ? $token->token : false; - } - - public function getGatewayLink() - { - $token = $this->getGatewayToken(); - return $token ? "https://dashboard.stripe.com/customers/{$token}" : false; - } - public function getCurrencyId() { if ($this->currency_id) { @@ -264,6 +212,14 @@ class Vendor extends EntityModel return $this->account->currency_id ?: DEFAULT_CURRENCY; } + + public function getTotalExpense() + { + return DB::table('expenses') + ->where('vendor_id', '=', $this->id) + ->whereNull('deleted_at') + ->sum('amount'); + } } Vendor::creating(function ($vendor) { diff --git a/app/Ninja/Repositories/ExpenseActivityRepository.php b/app/Ninja/Repositories/ExpenseActivityRepository.php index e1baed24c857..1406983962b5 100644 --- a/app/Ninja/Repositories/ExpenseActivityRepository.php +++ b/app/Ninja/Repositories/ExpenseActivityRepository.php @@ -15,7 +15,7 @@ class ExpenseActivityRepository { // init activity and copy over context $activity = self::getBlank($entity); - + // Fill with our information $activity->vendor_id = $entity->vendor_id; $activity->contact_id = $entity->contact_id; @@ -38,14 +38,15 @@ class ExpenseActivityRepository $activity->user_id = $entity->user_id; $activity->account_id = $entity->account_id; } - + $activity->token_id = session('token_id'); $activity->ip = Request::getClientIp(); + return $activity; } - - + + public function findByExpenseId($expenseId) { return DB::table('expense_activities') @@ -61,4 +62,4 @@ class ExpenseActivityRepository ); } -} \ No newline at end of file +} diff --git a/app/Ninja/Repositories/VendorRepository.php b/app/Ninja/Repositories/VendorRepository.php index 252f8fe2fe49..ae2a7f60a5e5 100644 --- a/app/Ninja/Repositories/VendorRepository.php +++ b/app/Ninja/Repositories/VendorRepository.php @@ -38,7 +38,6 @@ class VendorRepository extends BaseRepository 'vendor_contacts.first_name', 'vendor_contacts.last_name', 'vendors.balance', - //'vendors.last_login', 'vendors.created_at', 'vendors.work_phone', 'vendor_contacts.email', @@ -61,7 +60,7 @@ class VendorRepository extends BaseRepository return $query; } - + public function save($data) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; @@ -75,28 +74,18 @@ class VendorRepository extends BaseRepository $vendor->fill($data); $vendor->save(); - - if ( ! isset($data['vendor_contact']) && ! isset($data['vendor_contacts'])) { + if ( ! isset($data['vendorcontact']) && ! isset($data['vendorcontacts'])) { return $vendor; } - - + $first = true; - $vendorcontacts = isset($data['vendor_contact']) ? [$data['vendor_contact']] : $data['vendor_contacts']; - $vendorcontactIds = []; + $vendorcontacts = isset($data['vendorcontact']) ? [$data['vendorcontact']] : $data['vendorcontacts']; foreach ($vendorcontacts as $vendorcontact) { $vendorcontact = $vendor->addVendorContact($vendorcontact, $first); - $vendorcontactIds[] = $vendorcontact->public_id; $first = false; } - foreach ($vendor->vendorcontacts as $vendorcontact) { - if (!in_array($vendorcontact->public_id, $vendorcontactIds)) { - $vendorcontact->delete(); - } - } - return $vendor; } } diff --git a/app/Services/VendorActivityService.php b/app/Services/VendorActivityService.php index 9ad1cfb2c8a4..b083279a11c6 100644 --- a/app/Services/VendorActivityService.php +++ b/app/Services/VendorActivityService.php @@ -39,12 +39,9 @@ class VendorActivityService extends BaseService function ($model) { $data = [ 'vendor' => link_to('/vendors/' . $model->vendor_public_id, Utils::getVendorDisplayName($model)), - 'user' => $model->is_system ? '' . trans('texts.system') . '' : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email), - 'invoice' => $model->invoice ? link_to('/invoices/' . $model->invoice_public_id, $model->is_recurring ? trans('texts.recurring_invoice') : $model->invoice) : null, - 'quote' => $model->invoice ? link_to('/quotes/' . $model->invoice_public_id, $model->invoice) : null, + 'user' => $model->is_system ? '' . trans('texts.system') . '' : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email), 'contact' => $model->contact_id ? link_to('/vendors/' . $model->vendor_public_id, Utils::getVendorDisplayName($model)) : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email), - 'payment' => $model->payment ?: '', - 'credit' => Utils::formatMoney($model->credit, $model->currency_id, $model->country_id) + 'balance' => Utils::formatMoney($model->balance, $model->currency_id, $model->country_id) ]; return trans("texts.activity_{$model->activity_type_id}", $data); @@ -64,4 +61,4 @@ class VendorActivityService extends BaseService ] ]; } -} \ No newline at end of file +} diff --git a/app/Services/VendorService.php b/app/Services/VendorService.php index 53088ae25b2c..e67384ed6b06 100644 --- a/app/Services/VendorService.php +++ b/app/Services/VendorService.php @@ -6,7 +6,7 @@ use Auth; use App\Services\BaseService; use App\Ninja\Repositories\VendorRepository; use App\Ninja\Repositories\NinjaRepository; -// vendor + class VendorService extends BaseService { protected $vendorRepo; @@ -14,8 +14,8 @@ class VendorService extends BaseService public function __construct(VendorRepository $vendorRepo, DatatableService $datatableService, NinjaRepository $ninjaRepo) { - $this->vendorRepo = $vendorRepo; - $this->ninjaRepo = $ninjaRepo; + $this->vendorRepo = $vendorRepo; + $this->ninjaRepo = $ninjaRepo; $this->datatableService = $datatableService; } @@ -67,12 +67,6 @@ class VendorService extends BaseService return Utils::timestampToDateString(strtotime($model->created_at)); } ], - /*[ - 'last_login', - function ($model) { - return Utils::timestampToDateString(strtotime($model->last_login)); - } - ],*/ [ 'balance', function ($model) { diff --git a/composer.json b/composer.json index a7417f9deaf3..6514d2617e2d 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "omnipay/omnipay": "~2.3.0", "intervention/image": "dev-master", "webpatser/laravel-countries": "dev-master", - "barryvdh/laravel-ide-helper": "2.0.x", + "barryvdh/laravel-ide-helper": "^2.1", "doctrine/dbal": "2.5.x", "jsanc623/phpbenchtime": "2.x", "lokielse/omnipay-alipay": "dev-master", diff --git a/composer.lock b/composer.lock index c70a49e7ffe4..6a19f28aa1ee 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "71b1d7519dd65f8e028c1c417354606d", - "content-hash": "7305e2f5d6864894aeb23ac91f3e1fe7", + "hash": "67f1ec18911e60b4f0babd8991278fda", + "content-hash": "862c124e81438b922db1afe626149ec0", "packages": [ { "name": "agmscode/omnipay-agms", @@ -123,12 +123,12 @@ "source": { "type": "git", "url": "https://github.com/alfaproject/omnipay-skrill.git", - "reference": "2fa2ba8083fd5289366660f8de1b46b5f49ac052" + "reference": "41a7a03c5b90d496720e288bebc157d898837ccd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/alfaproject/omnipay-skrill/zipball/2fa2ba8083fd5289366660f8de1b46b5f49ac052", - "reference": "2fa2ba8083fd5289366660f8de1b46b5f49ac052", + "url": "https://api.github.com/repos/alfaproject/omnipay-skrill/zipball/41a7a03c5b90d496720e288bebc157d898837ccd", + "reference": "41a7a03c5b90d496720e288bebc157d898837ccd", "shasum": "" }, "require": { @@ -169,7 +169,7 @@ "payment", "skrill" ], - "time": "2014-02-25 13:40:07" + "time": "2016-01-13 16:33:07" }, { "name": "anahkiasen/former", @@ -379,22 +379,22 @@ }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.0.6", + "version": "v2.1.2", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "037386153630a7515a1542f29410d8c267651689" + "reference": "d82e8f191fb043a0f8cbf2de64fd3027bfa4f772" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/037386153630a7515a1542f29410d8c267651689", - "reference": "037386153630a7515a1542f29410d8c267651689", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/d82e8f191fb043a0f8cbf2de64fd3027bfa4f772", + "reference": "d82e8f191fb043a0f8cbf2de64fd3027bfa4f772", "shasum": "" }, "require": { - "illuminate/console": "5.0.x|5.1.x", - "illuminate/filesystem": "5.0.x|5.1.x", - "illuminate/support": "5.0.x|5.1.x", + "illuminate/console": "5.0.x|5.1.x|5.2.x", + "illuminate/filesystem": "5.0.x|5.1.x|5.2.x", + "illuminate/support": "5.0.x|5.1.x|5.2.x", "php": ">=5.4.0", "phpdocumentor/reflection-docblock": "2.0.4", "symfony/class-loader": "~2.3" @@ -408,7 +408,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -438,7 +438,7 @@ "phpstorm", "sublime" ], - "time": "2015-06-25 08:58:59" + "time": "2015-12-21 19:48:06" }, { "name": "cardgate/omnipay-cardgate", @@ -4363,16 +4363,16 @@ }, { "name": "omnipay/firstdata", - "version": "v2.2.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/thephpleague/omnipay-firstdata.git", - "reference": "0853bba0ee313f5557eb1c696d3ce5538dbd4aca" + "reference": "e33826821db88d90886cad6c81a29452d3cf91a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/omnipay-firstdata/zipball/0853bba0ee313f5557eb1c696d3ce5538dbd4aca", - "reference": "0853bba0ee313f5557eb1c696d3ce5538dbd4aca", + "url": "https://api.github.com/repos/thephpleague/omnipay-firstdata/zipball/e33826821db88d90886cad6c81a29452d3cf91a2", + "reference": "e33826821db88d90886cad6c81a29452d3cf91a2", "shasum": "" }, "require": { @@ -4417,7 +4417,7 @@ "pay", "payment" ], - "time": "2015-07-28 17:50:44" + "time": "2016-01-14 06:24:28" }, { "name": "omnipay/gocardless", @@ -5112,16 +5112,16 @@ }, { "name": "omnipay/paypal", - "version": "2.5.0", + "version": "v2.5.1", "source": { "type": "git", "url": "https://github.com/thephpleague/omnipay-paypal.git", - "reference": "67efe5a927dec13fc7520e29bc44f15fd3a728e9" + "reference": "b546d24241725061d44e60516f0fbce202336963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/omnipay-paypal/zipball/67efe5a927dec13fc7520e29bc44f15fd3a728e9", - "reference": "67efe5a927dec13fc7520e29bc44f15fd3a728e9", + "url": "https://api.github.com/repos/thephpleague/omnipay-paypal/zipball/b546d24241725061d44e60516f0fbce202336963", + "reference": "b546d24241725061d44e60516f0fbce202336963", "shasum": "" }, "require": { @@ -5166,20 +5166,20 @@ "paypal", "purchase" ], - "time": "2015-11-11 21:48:00" + "time": "2016-01-13 07:03:27" }, { "name": "omnipay/pin", - "version": "v2.1.0", + "version": "v2.2.1", "source": { "type": "git", - "url": "https://github.com/omnipay/pin.git", - "reference": "04e778e9689882d4c40419263014068b69b93168" + "url": "https://github.com/thephpleague/omnipay-pin.git", + "reference": "c2252e41f3674267b2bbe79eaeec73b6b1e4ee58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/omnipay/pin/zipball/04e778e9689882d4c40419263014068b69b93168", - "reference": "04e778e9689882d4c40419263014068b69b93168", + "url": "https://api.github.com/repos/thephpleague/omnipay-pin/zipball/c2252e41f3674267b2bbe79eaeec73b6b1e4ee58", + "reference": "c2252e41f3674267b2bbe79eaeec73b6b1e4ee58", "shasum": "" }, "require": { @@ -5210,11 +5210,11 @@ }, { "name": "Omnipay Contributors", - "homepage": "https://github.com/omnipay/pin/contributors" + "homepage": "https://github.com/thephpleague/omnipay-pin/contributors" } ], "description": "Pin Payments driver for the Omnipay payment processing library", - "homepage": "https://github.com/omnipay/pin", + "homepage": "https://github.com/thephpleague/omnipay-pin", "keywords": [ "gateway", "merchant", @@ -5223,20 +5223,20 @@ "payment", "pin" ], - "time": "2014-04-14 11:26:15" + "time": "2016-01-13 07:00:17" }, { "name": "omnipay/sagepay", - "version": "v2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/thephpleague/omnipay-sagepay.git", - "reference": "899507095428fa54276ba5ca89f11fd7f8fd78ab" + "reference": "4208d23b33b2f8a59176e44ad22d304c461ecb62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/omnipay-sagepay/zipball/899507095428fa54276ba5ca89f11fd7f8fd78ab", - "reference": "899507095428fa54276ba5ca89f11fd7f8fd78ab", + "url": "https://api.github.com/repos/thephpleague/omnipay-sagepay/zipball/4208d23b33b2f8a59176e44ad22d304c461ecb62", + "reference": "4208d23b33b2f8a59176e44ad22d304c461ecb62", "shasum": "" }, "require": { @@ -5282,7 +5282,7 @@ "sage pay", "sagepay" ], - "time": "2015-04-02 17:46:20" + "time": "2016-01-12 12:43:31" }, { "name": "omnipay/securepay", @@ -5343,16 +5343,16 @@ }, { "name": "omnipay/stripe", - "version": "v2.3.0", + "version": "v2.3.1", "source": { "type": "git", "url": "https://github.com/thephpleague/omnipay-stripe.git", - "reference": "54b816a5e95e34c988d71fb805b0232cfd7c1ce5" + "reference": "6c4cef5b5168a58476eef6fa73b7875e15c94b6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/omnipay-stripe/zipball/54b816a5e95e34c988d71fb805b0232cfd7c1ce5", - "reference": "54b816a5e95e34c988d71fb805b0232cfd7c1ce5", + "url": "https://api.github.com/repos/thephpleague/omnipay-stripe/zipball/6c4cef5b5168a58476eef6fa73b7875e15c94b6f", + "reference": "6c4cef5b5168a58476eef6fa73b7875e15c94b6f", "shasum": "" }, "require": { @@ -5396,7 +5396,7 @@ "payment", "stripe" ], - "time": "2015-11-10 16:17:35" + "time": "2016-01-13 04:00:45" }, { "name": "omnipay/targetpay", @@ -5512,6 +5512,54 @@ ], "time": "2014-09-17 00:37:18" }, + { + "name": "paragonie/random_compat", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/dd8998b7c846f6909f4e7a5f67fabebfc412a4f7", + "reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2016-01-06 13:31:20" + }, { "name": "patricktalmadge/bootstrapper", "version": "5.5.3", @@ -6058,16 +6106,16 @@ }, { "name": "symfony/class-loader", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "ec74b0a279cf3a9bd36172b3e3061591d380ce6c" + "reference": "98e9089a428ed0e39423b67352c57ef5910a3269" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/ec74b0a279cf3a9bd36172b3e3061591d380ce6c", - "reference": "ec74b0a279cf3a9bd36172b3e3061591d380ce6c", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/98e9089a428ed0e39423b67352c57ef5910a3269", + "reference": "98e9089a428ed0e39423b67352c57ef5910a3269", "shasum": "" }, "require": { @@ -6106,11 +6154,11 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2015-12-05 17:37:59" + "time": "2016-01-03 15:33:41" }, { "name": "symfony/console", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/Console", "source": { "type": "git", @@ -6168,16 +6216,16 @@ }, { "name": "symfony/css-selector", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "eaa3320e32f09a01dc432c6efbe8051aee59cfef" + "reference": "ac06d8173bd80790536c0a4a634a7d705b91f54f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/eaa3320e32f09a01dc432c6efbe8051aee59cfef", - "reference": "eaa3320e32f09a01dc432c6efbe8051aee59cfef", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/ac06d8173bd80790536c0a4a634a7d705b91f54f", + "reference": "ac06d8173bd80790536c0a4a634a7d705b91f54f", "shasum": "" }, "require": { @@ -6217,11 +6265,11 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2015-12-05 17:37:59" + "time": "2016-01-03 15:33:41" }, { "name": "symfony/debug", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/Debug", "source": { "type": "git", @@ -6282,16 +6330,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc" + "reference": "ee278f7c851533e58ca307f66305ccb9188aceda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc", - "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ee278f7c851533e58ca307f66305ccb9188aceda", + "reference": "ee278f7c851533e58ca307f66305ccb9188aceda", "shasum": "" }, "require": { @@ -6338,20 +6386,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-10-30 20:15:42" + "time": "2016-01-13 10:28:07" }, { "name": "symfony/filesystem", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc" + "reference": "637b64d0ee10f44ae98dbad651b1ecdf35a11e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/a7ad724530a764d70c168d321ac226ba3d2f10fc", - "reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/637b64d0ee10f44ae98dbad651b1ecdf35a11e8c", + "reference": "637b64d0ee10f44ae98dbad651b1ecdf35a11e8c", "shasum": "" }, "require": { @@ -6387,11 +6435,11 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2015-12-22 10:25:57" + "time": "2016-01-13 10:28:07" }, { "name": "symfony/finder", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/Finder", "source": { "type": "git", @@ -6441,7 +6489,7 @@ }, { "name": "symfony/http-foundation", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/HttpFoundation", "source": { "type": "git", @@ -6495,17 +6543,17 @@ }, { "name": "symfony/http-kernel", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/HttpKernel", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6" + "reference": "cdd991d304fed833514dc44d6aafcf19397c26cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6", - "reference": "498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/cdd991d304fed833514dc44d6aafcf19397c26cb", + "reference": "cdd991d304fed833514dc44d6aafcf19397c26cb", "shasum": "" }, "require": { @@ -6569,7 +6617,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2015-11-23 11:37:53" + "time": "2016-01-14 10:11:16" }, { "name": "symfony/polyfill-php56", @@ -6681,7 +6729,7 @@ }, { "name": "symfony/process", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/Process", "source": { "type": "git", @@ -6731,7 +6779,7 @@ }, { "name": "symfony/routing", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/Routing", "source": { "type": "git", @@ -6800,20 +6848,21 @@ }, { "name": "symfony/security-core", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/Security/Core", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "05f58bb3814e8a853332dc448e3b7addaa87679c" + "reference": "813cf2aaacccbbe1a4705aef8d4ac0d79d993a76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/05f58bb3814e8a853332dc448e3b7addaa87679c", - "reference": "05f58bb3814e8a853332dc448e3b7addaa87679c", + "url": "https://api.github.com/repos/symfony/security-core/zipball/813cf2aaacccbbe1a4705aef8d4ac0d79d993a76", + "reference": "813cf2aaacccbbe1a4705aef8d4ac0d79d993a76", "shasum": "" }, "require": { + "paragonie/random_compat": "~1.0", "php": ">=5.3.3" }, "require-dev": { @@ -6860,11 +6909,11 @@ ], "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", - "time": "2015-07-22 10:08:40" + "time": "2016-01-14 09:04:34" }, { "name": "symfony/translation", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/Translation", "source": { "type": "git", @@ -6923,7 +6972,7 @@ }, { "name": "symfony/var-dumper", - "version": "v2.6.12", + "version": "v2.6.13", "target-dir": "Symfony/Component/VarDumper", "source": { "type": "git", @@ -7347,16 +7396,16 @@ }, { "name": "zircote/swagger-php", - "version": "2.0.4", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/zircote/swagger-php.git", - "reference": "be5d96e56c23cbe52c5bc5e267851323d95c57cd" + "reference": "c19af4edcc13c00e82fabeee926335b1fe1d92e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/be5d96e56c23cbe52c5bc5e267851323d95c57cd", - "reference": "be5d96e56c23cbe52c5bc5e267851323d95c57cd", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/c19af4edcc13c00e82fabeee926335b1fe1d92e9", + "reference": "c19af4edcc13c00e82fabeee926335b1fe1d92e9", "shasum": "" }, "require": { @@ -7403,7 +7452,7 @@ "rest", "service discovery" ], - "time": "2015-11-13 13:50:11" + "time": "2016-01-15 09:39:28" } ], "packages-dev": [ @@ -8599,16 +8648,16 @@ }, { "name": "symfony/browser-kit", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "dd2cfb20fabd4efca14cf3b2345d40b3dd5e9aca" + "reference": "a93dffaf763182acad12a4c42c7efc372899891e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/dd2cfb20fabd4efca14cf3b2345d40b3dd5e9aca", - "reference": "dd2cfb20fabd4efca14cf3b2345d40b3dd5e9aca", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/a93dffaf763182acad12a4c42c7efc372899891e", + "reference": "a93dffaf763182acad12a4c42c7efc372899891e", "shasum": "" }, "require": { @@ -8652,20 +8701,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2015-12-26 13:37:56" + "time": "2016-01-12 17:46:01" }, { "name": "symfony/dom-crawler", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "a2712aff8b250d9601ad6bd23a2ff82a12730e8e" + "reference": "650d37aacb1fa0dcc24cced483169852b3a0594e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/a2712aff8b250d9601ad6bd23a2ff82a12730e8e", - "reference": "a2712aff8b250d9601ad6bd23a2ff82a12730e8e", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/650d37aacb1fa0dcc24cced483169852b3a0594e", + "reference": "650d37aacb1fa0dcc24cced483169852b3a0594e", "shasum": "" }, "require": { @@ -8708,7 +8757,7 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2015-12-23 17:16:29" + "time": "2016-01-03 15:33:41" }, { "name": "symfony/polyfill-mbstring", @@ -8771,16 +8820,16 @@ }, { "name": "symfony/yaml", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966" + "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966", - "reference": "ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966", + "url": "https://api.github.com/repos/symfony/yaml/zipball/34c8a4b51e751e7ea869b8262f883d008a2b81b8", + "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8", "shasum": "" }, "require": { @@ -8816,7 +8865,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-12-26 13:37:56" + "time": "2016-01-13 10:28:07" } ], "aliases": [], diff --git a/config/app.php b/config/app.php index a7e071e62898..cd55e8ee965a 100644 --- a/config/app.php +++ b/config/app.php @@ -161,6 +161,8 @@ return [ 'App\Providers\ConfigServiceProvider', 'App\Providers\EventServiceProvider', 'App\Providers\RouteServiceProvider', + + 'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider', ], /* diff --git a/database/migrations/2016_01_04_175228_create_vendors_table.php b/database/migrations/2016_01_04_175228_create_vendors_table.php index 88b0f480f497..1ca1df6ae6f1 100644 --- a/database/migrations/2016_01_04_175228_create_vendors_table.php +++ b/database/migrations/2016_01_04_175228_create_vendors_table.php @@ -27,14 +27,10 @@ class CreateVendorsTable extends Migration { $table->string('city'); $table->string('state'); $table->string('postal_code'); - $table->integer('country_id')->default(0); + $table->unsignedInteger('country_id')->nullable(); $table->string('work_phone'); $table->text('private_notes'); $table->decimal('balance',13,2); - $table->decimal('paid_to_date',13,2); - - //$table->dateTime('last_login'); - $table->string('website'); $table->integer('industry_id')->nullable(); $table->integer('size_id')->nullable(); @@ -45,7 +41,7 @@ class CreateVendorsTable extends Migration { $table->string('custom_value2')->nullable(); $table->string('vat_number')->nullable(); $table->string('id_number')->nullable(); - $table->integer('language_id', false, true)->nullable(); + $table->integer('language_id', false, true)->nullable(); }); // add relations diff --git a/invoiceninja.komodoproject b/invoiceninja.komodoproject index 9f6ba7068341..16111c8ee84d 100644 --- a/invoiceninja.komodoproject +++ b/invoiceninja.komodoproject @@ -35,7 +35,7 @@ None None c:\wamp\bin\php\php5.6.15\php.exe - C:\webdev\invoiceninja;C:\webdev\invoiceninja\lavarelcodecomplete + vendor;C:/webdev/invoiceninja/app 1 None None diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 7f404a4bedd1..c83b79484f9a 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -476,6 +476,7 @@ ->label('client_name') !!} + {!! Former::text('client[id_number]') ->label('id_number') ->data_bind("value: id_number, valueUpdate: 'afterkeydown'") !!} @@ -489,6 +490,7 @@ {!! Former::text('client[work_phone]') ->label('work_phone') ->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!} + @if (Auth::user()->isPro()) diff --git a/resources/views/vendors/show.blade.php b/resources/views/vendors/show.blade.php index 7be091d7e9e0..bf7040c6d8e0 100644 --- a/resources/views/vendors/show.blade.php +++ b/resources/views/vendors/show.blade.php @@ -28,10 +28,6 @@ {!! Former::text('public_id')->value($vendor->public_id) !!}
    - @if ($gatewayLink) - {!! Button::normal(trans('texts.view_in_stripe'))->asLinkTo($gatewayLink)->withAttributes(['target' => '_blank']) !!} - @endif - @if ($vendor->trashed()) {!! Button::primary(trans('texts.restore_vendor'))->withAttributes(['onclick' => 'onRestoreClick()']) !!} @else @@ -53,17 +49,9 @@

    {{ $vendor->getDisplayName() }}

    - {{-- - @if ($vendor->last_login > 0) -

    - {{ trans('texts.last_logged_in') }} {{ Utils::timestampToDateTimeString(strtotime($vendor->last_login)) }} -

    - @endif - --}}
    -

    {{ trans('texts.details') }}

    @if ($vendor->id_number) @@ -137,20 +125,10 @@

    {{ trans('texts.standing') }} - - - - - + - @if ($credit > 0) - - - - - @endif
    {{ trans('texts.paid_to_date') }}{{ Utils::formatMoney($vendor->paid_to_date, $vendor->getCurrencyId()) }}
    {{ trans('texts.balance') }}{{ Utils::formatMoney($vendor->balance, $vendor->getCurrencyId()) }}{{ Utils::formatMoney($totalexpense, $vendor->getCurrencyId()) }}
    {{ trans('texts.credit') }}{{ Utils::formatMoney($credit, $vendor->getCurrencyId()) }}

    @@ -165,43 +143,36 @@
    - {!! Datatable::table() ->addColumn( trans('texts.date'), - trans('texts.message'), - trans('texts.balance'), - trans('texts.adjustment')) + trans('texts.message')) ->setUrl(url('api/vendoractivities/'. $vendor->public_id)) ->setCustomValues('entityType', 'activity') ->setOptions('sPaginationType', 'bootstrap') ->setOptions('bFilter', false) ->setOptions('aaSorting', [['0', 'desc']]) ->render('datatable') !!} -
    - {!! Datatable::table() ->addColumn( - trans('texts.credit_amount'), - trans('texts.credit_balance'), - trans('texts.credit_date'), + trans('texts.expense_date'), + trans('texts.amount'), trans('texts.private_notes')) - ->setUrl(url('api/expenses/' . $vendor->public_id)) - ->setCustomValues('entityType', 'credits') + ->setUrl(url('api/expense/' . $vendor->public_id)) + ->setCustomValues('entityType', 'expenses') ->setOptions('sPaginationType', 'bootstrap') ->setOptions('bFilter', false) ->setOptions('aaSorting', [['0', 'asc']]) ->render('datatable') !!} -
    @@ -214,7 +185,7 @@ window.location = '{{ URL::to('vendors/' . $vendor->public_id . '/edit') }}'; }); $('.primaryDropDown:not(.dropdown-toggle)').click(function() { - window.location = '{{ URL::to('invoices/create/' . $vendor->public_id ) }}'; + window.location = '{{ URL::to('expenses/create/' . $vendor->public_id ) }}'; }); // load datatable data when tab is shown and remember last tab selected @@ -225,9 +196,6 @@ if (!loadedTabs.hasOwnProperty(target)) { loadedTabs[target] = true; window['load_' + target](); - if (target == 'invoices' && window.hasOwnProperty('load_recurring_invoices')) { - window['load_recurring_invoices'](); - } } }); var tab = localStorage.getItem('vendor_tab'); From 2525f68a39dbcf8f0f40f19bf1a2a709cfaab3cf Mon Sep 17 00:00:00 2001 From: steenrabol Date: Tue, 19 Jan 2016 20:35:15 +0100 Subject: [PATCH 20/20] 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)