From 3e9e28f06f8c6d0465e64da5487eef85e9fa5f95 Mon Sep 17 00:00:00 2001 From: steenrabol Date: Sun, 10 Jan 2016 11:25:05 +0100 Subject: [PATCH] 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 +