mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Enabled contact selection when sending email
This commit is contained in:
parent
cf7106e8ac
commit
19abec6f09
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
/app/config/development
|
||||
/app/storage/
|
||||
/public/logo
|
||||
/public/build
|
||||
/bootstrap/compiled.php
|
||||
/vendor
|
||||
composer.phar
|
||||
|
@ -24,7 +24,7 @@ class SendRecurringInvoices extends Command {
|
||||
|
||||
$today = new DateTime();
|
||||
|
||||
$invoices = Invoice::with('account', 'invoice_items')->whereRaw('frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', array($today, $today))->get();
|
||||
$invoices = Invoice::with('account', 'invoice_items')->whereRaw('is_recurring is true AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', array($today, $today))->get();
|
||||
$this->info(count($invoices) . ' recurring invoice(s) found');
|
||||
|
||||
foreach ($invoices as $recurInvoice)
|
||||
@ -59,7 +59,7 @@ class SendRecurringInvoices extends Command {
|
||||
$recurInvoice->last_sent_date = new DateTime();
|
||||
$recurInvoice->save();
|
||||
|
||||
$this->mailer->sendInvoice($invoice, $invoice->client->contacts()->first());
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
}
|
||||
|
||||
$this->info('Done');
|
||||
|
@ -21,7 +21,7 @@ class InvoiceController extends \BaseController {
|
||||
'columns'=>['checkbox', 'Invoice Number', 'Client', 'Invoice Date', 'Invoice Total', 'Balance Due', 'Due Date', 'Status', 'Action']
|
||||
];
|
||||
|
||||
if (Invoice::scope()->where('frequency_id', '>', '0')->count() > 0)
|
||||
if (Invoice::scope()->where('is_recurring', '=', true)->count() > 0)
|
||||
{
|
||||
$data['secEntityType'] = ENTITY_RECURRING_INVOICE;
|
||||
$data['secColumns'] = ['checkbox', 'Frequency', 'Client', 'Start Date', 'End Date', 'Invoice Total', 'Action'];
|
||||
@ -37,7 +37,7 @@ class InvoiceController extends \BaseController {
|
||||
->join('invoice_statuses', 'invoice_statuses.id', '=', 'invoices.invoice_status_id')
|
||||
->where('invoices.account_id', '=', Auth::user()->account_id)
|
||||
->where('invoices.deleted_at', '=', null)
|
||||
->where('invoices.frequency_id', '=', 0)
|
||||
->where('invoices.is_recurring', '=', false)
|
||||
->select('clients.public_id as client_public_id', 'invoice_number', 'clients.name as client_name', 'invoices.public_id', 'total', 'invoices.balance', 'invoice_date', 'due_date', 'invoice_statuses.name as invoice_status_name');
|
||||
|
||||
if ($clientPublicId) {
|
||||
@ -97,7 +97,7 @@ class InvoiceController extends \BaseController {
|
||||
->join('frequencies', 'frequencies.id', '=', 'invoices.frequency_id')
|
||||
->where('invoices.account_id', '=', Auth::user()->account_id)
|
||||
->where('invoices.deleted_at', '=', null)
|
||||
->where('invoices.frequency_id', '>', 0)
|
||||
->where('invoices.is_recurring', '=', true)
|
||||
->select('clients.public_id as client_public_id', 'clients.name as client_name', 'invoices.public_id', 'total', 'frequencies.name as frequency', 'start_date', 'end_date');
|
||||
|
||||
if ($clientPublicId) {
|
||||
@ -322,10 +322,20 @@ class InvoiceController extends \BaseController {
|
||||
$invoice = Invoice::scope($publicId)->with('account.country', 'client', 'invoice_items')->firstOrFail();
|
||||
Utils::trackViewed($invoice->invoice_number . ' - ' . $invoice->client->name, ENTITY_INVOICE);
|
||||
|
||||
$contactIds = DB::table('invitations')
|
||||
->join('contacts', 'contacts.id', '=','invitations.contact_id')
|
||||
->where('invitations.invoice_id', '=', $invoice->id)
|
||||
->where('invitations.account_id', '=', Auth::user()->account_id)
|
||||
->where('invitations.deleted_at', '=', null)
|
||||
->select('contacts.public_id')->lists('public_id');
|
||||
|
||||
$data = array(
|
||||
'account' => $invoice->account,
|
||||
'invoice' => $invoice,
|
||||
'method' => 'PUT',
|
||||
'invitationContactIds' => $contactIds,
|
||||
'clientSizes' => ClientSize::orderBy('id')->get(),
|
||||
'clientIndustries' => ClientIndustry::orderBy('name')->get(),
|
||||
'url' => 'invoices/' . $publicId,
|
||||
'title' => '- ' . $invoice->invoice_number,
|
||||
'client' => $invoice->client);
|
||||
@ -349,6 +359,8 @@ class InvoiceController extends \BaseController {
|
||||
'invoiceNumber' => $invoiceNumber,
|
||||
'method' => 'POST',
|
||||
'url' => 'invoices',
|
||||
'clientSizes' => ClientSize::orderBy('id')->get(),
|
||||
'clientIndustries' => ClientIndustry::orderBy('name')->get(),
|
||||
'title' => '- New Invoice',
|
||||
'client' => $client,
|
||||
'items' => json_decode(Input::old('items')));
|
||||
@ -393,20 +405,18 @@ class InvoiceController extends \BaseController {
|
||||
{
|
||||
return InvoiceController::bulk();
|
||||
}
|
||||
|
||||
$rules = array(
|
||||
'client' => 'required',
|
||||
);
|
||||
$validator = Validator::make(Input::all(), $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
$input = json_decode(Input::get('data'));
|
||||
$inputClient = $input->client;
|
||||
$inputClient->name = trim($inputClient->name);
|
||||
|
||||
if (!$inputClient->name) {
|
||||
return Redirect::to('invoices/create')
|
||||
->withInput()
|
||||
->withErrors($validator);
|
||||
->withInput();
|
||||
} else {
|
||||
|
||||
$clientPublicId = Input::get('client');
|
||||
|
||||
$clientPublicId = $input->client->public_id;
|
||||
|
||||
if ($clientPublicId == "-1")
|
||||
{
|
||||
$client = Client::createNew();
|
||||
@ -418,24 +428,58 @@ class InvoiceController extends \BaseController {
|
||||
$client = Client::scope($clientPublicId)->with('contacts')->firstOrFail();
|
||||
$contact = $client->contacts()->where('is_primary', '=', true)->firstOrFail();
|
||||
}
|
||||
|
||||
$client->name = trim(Input::get('name'));
|
||||
$client->work_phone = trim(Input::get('work_phone'));
|
||||
$client->address1 = trim(Input::get('address1'));
|
||||
$client->address2 = trim(Input::get('address2'));
|
||||
$client->city = trim(Input::get('city'));
|
||||
$client->state = trim(Input::get('state'));
|
||||
$client->postal_code = trim(Input::get('postal_code'));
|
||||
if (Input::get('country_id')) {
|
||||
$client->country_id = Input::get('country_id');
|
||||
}
|
||||
|
||||
$inputClient = $input->client;
|
||||
$client->name = trim($inputClient->name);
|
||||
$client->work_phone = trim($inputClient->work_phone);
|
||||
$client->address1 = trim($inputClient->address1);
|
||||
$client->address2 = trim($inputClient->address2);
|
||||
$client->city = trim($inputClient->city);
|
||||
$client->state = trim($inputClient->state);
|
||||
$client->postal_code = trim($inputClient->postal_code);
|
||||
$client->country_id = $inputClient->country_id ? $inputClient->country_id : null;
|
||||
$client->save();
|
||||
|
||||
$contact->first_name = trim(Input::get('first_name'));
|
||||
$contact->last_name = trim(Input::get('last_name'));
|
||||
$contact->phone = trim(Input::get('phone'));
|
||||
$contact->email = trim(Input::get('email'));
|
||||
$client->contacts()->save($contact);
|
||||
$isPrimary = true;
|
||||
$contacts = [];
|
||||
$contactIds = [];
|
||||
$sendInvoiceIds = [];
|
||||
|
||||
foreach ($inputClient->contacts as $contact)
|
||||
{
|
||||
if (isset($contact->public_id) && $contact->public_id)
|
||||
{
|
||||
$record = Contact::scope($contact->public_id)->firstOrFail();
|
||||
}
|
||||
else
|
||||
{
|
||||
$record = Contact::createNew();
|
||||
}
|
||||
|
||||
$record->email = trim($contact->email);
|
||||
$record->first_name = trim($contact->first_name);
|
||||
$record->last_name = trim($contact->last_name);
|
||||
$record->phone = trim($contact->phone);
|
||||
$record->is_primary = $isPrimary;
|
||||
$isPrimary = false;
|
||||
|
||||
$client->contacts()->save($record);
|
||||
$contacts[] = $record;
|
||||
$contactIds[] = $record->public_id;
|
||||
|
||||
if ($contact->send_invoice)
|
||||
{
|
||||
$sendInvoiceIds[] = $record->id;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($client->contacts as $contact)
|
||||
{
|
||||
if (!in_array($contact->public_id, $contactIds))
|
||||
{
|
||||
$contact->forceDelete();
|
||||
}
|
||||
}
|
||||
|
||||
if ($publicId) {
|
||||
$invoice = Invoice::scope($publicId)->firstOrFail();
|
||||
@ -445,20 +489,22 @@ class InvoiceController extends \BaseController {
|
||||
}
|
||||
|
||||
$invoice->client_id = $client->id;
|
||||
$invoice->discount = Input::get('discount');
|
||||
$invoice->invoice_number = trim(Input::get('invoice_number'));
|
||||
$invoice->invoice_date = Utils::toSqlDate(Input::get('invoice_date'));
|
||||
$invoice->due_date = Utils::toSqlDate(Input::get('due_date'));
|
||||
$invoice->discount = $input->discount;
|
||||
$invoice->invoice_number = trim($input->invoice_number);
|
||||
$invoice->invoice_date = Utils::toSqlDate($input->invoice_date);
|
||||
$invoice->due_date = Utils::toSqlDate($input->due_date);
|
||||
|
||||
$invoice->frequency_id = Input::get('recurring') ? Input::get('frequency') : 0;
|
||||
$invoice->start_date = Utils::toSqlDate(Input::get('start_date'));
|
||||
$invoice->end_date = Utils::toSqlDate(Input::get('end_date'));
|
||||
$invoice->notes = Input::get('notes');
|
||||
$invoice->po_number = Input::get('po_number');
|
||||
$invoice->is_recurring = $input->is_recurring;
|
||||
$invoice->frequency_id = $input->frequency_id ? $input->frequency_id : 0;
|
||||
$invoice->start_date = Utils::toSqlDate($input->start_date);
|
||||
$invoice->end_date = Utils::toSqlDate($input->end_date);
|
||||
$invoice->notes = $input->notes;
|
||||
$invoice->po_number = $input->po_number;
|
||||
|
||||
|
||||
$client->invoices()->save($invoice);
|
||||
|
||||
$items = json_decode(Input::get('items'));
|
||||
$items = $input->invoice_items;
|
||||
$total = 0;
|
||||
|
||||
foreach ($items as $item)
|
||||
@ -484,6 +530,24 @@ class InvoiceController extends \BaseController {
|
||||
$invoice->total = $total;
|
||||
$invoice->save();
|
||||
|
||||
foreach ($contacts as $contact)
|
||||
{
|
||||
$invitation = Invitation::scope()->whereContactId($contact->id)->whereInvoiceId($invoice->id)->first();
|
||||
|
||||
if (in_array($contact->id, $sendInvoiceIds) && !$invitation)
|
||||
{
|
||||
$invitation = Invitation::createNew();
|
||||
$invitation->invoice_id = $invoice->id;
|
||||
$invitation->contact_id = $contact->id;
|
||||
$invitation->invitation_key = str_random(20);
|
||||
$invitation->save();
|
||||
}
|
||||
else if (!in_array($contact->id, $sendInvoiceIds) && $invitation)
|
||||
{
|
||||
$invitation->forceDelete();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($items as $item)
|
||||
{
|
||||
if (!$item->cost && !$item->qty && !$item->product_key && !$item->notes)
|
||||
@ -529,7 +593,7 @@ class InvoiceController extends \BaseController {
|
||||
}
|
||||
else if ($action == 'email')
|
||||
{
|
||||
$this->mailer->sendInvoice($invoice, $contact);
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
|
||||
Session::flash('message', 'Successfully emailed invoice');
|
||||
} else {
|
||||
@ -594,12 +658,12 @@ class InvoiceController extends \BaseController {
|
||||
$invoice = Invoice::with('invoice_items')->scope($publicId)->firstOrFail();
|
||||
|
||||
$clone = Invoice::createNew();
|
||||
foreach (['client_id', 'discount', 'invoice_date', 'due_date', 'frequency_id', 'start_date', 'end_date', 'notes'] as $field)
|
||||
foreach (['client_id', 'discount', 'invoice_date', 'due_date', 'is_recurring', 'frequency_id', 'start_date', 'end_date', 'notes'] as $field)
|
||||
{
|
||||
$clone->$field = $invoice->$field;
|
||||
}
|
||||
|
||||
if (!$clone->isRecurring())
|
||||
if (!$clone->is_recurring)
|
||||
{
|
||||
$clone->invoice_number = Auth::user()->account->getNextInvoiceNumber();
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->string('last_name');
|
||||
$t->string('email');
|
||||
$t->string('phone');
|
||||
$t->timestamp('last_login');
|
||||
$t->timestamp('last_login');
|
||||
|
||||
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
|
||||
$t->foreign('user_id')->references('id')->on('users');
|
||||
@ -238,14 +238,15 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->string('invoice_number');
|
||||
$t->float('discount');
|
||||
$t->string('po_number');
|
||||
$t->date('invoice_date');
|
||||
$t->date('invoice_date')->nullable();
|
||||
$t->date('due_date')->nullable();
|
||||
$t->text('notes');
|
||||
|
||||
$t->boolean('is_recurring');
|
||||
$t->unsignedInteger('frequency_id');
|
||||
$t->date('start_date')->nullable();
|
||||
$t->date('end_date')->nullable();
|
||||
$t->date('last_sent_date')->nullable();
|
||||
$t->timestamp('last_sent_date')->nullable();
|
||||
$t->unsignedInteger('recurring_invoice_id')->index()->nullable();
|
||||
|
||||
|
||||
@ -274,6 +275,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->timestamps();
|
||||
$t->softDeletes();
|
||||
|
||||
$t->timestamp('sent_date');
|
||||
$t->timestamp('viewed_date');
|
||||
|
||||
$t->foreign('user_id')->references('id')->on('users');
|
||||
|
@ -8,30 +8,24 @@ use Auth;
|
||||
|
||||
class ContactMailer extends Mailer {
|
||||
|
||||
public function sendInvoice(Invoice $invoice, Contact $contact)
|
||||
public function sendInvoice(Invoice $invoice)
|
||||
{
|
||||
$view = 'invoice';
|
||||
$data = array('link' => URL::to('view') . '/' . $invoice->invoice_key);
|
||||
$subject = '';
|
||||
|
||||
if (Auth::check()) {
|
||||
$invitation = Invitation::createNew();
|
||||
} else {
|
||||
$invitation = Invitation::createNew($invoice);
|
||||
}
|
||||
foreach ($invoice->invitations as $invitation)
|
||||
{
|
||||
//$invitation->date_sent =
|
||||
$invitation->save();
|
||||
|
||||
$invitation->invoice_id = $invoice->id;
|
||||
$invitation->user_id = Auth::check() ? Auth::user()->id : $invoice->user_id;
|
||||
$invitation->contact_id = $contact->id;
|
||||
$invitation->invitation_key = str_random(20);
|
||||
$invitation->save();
|
||||
$this->sendTo($invitation->contact->email, $subject, $view, $data);
|
||||
}
|
||||
|
||||
if (!$invoice->isSent())
|
||||
{
|
||||
$invoice->invoice_status_id = INVOICE_STATUS_SENT;
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
return $this->sendTo($contact->email, $subject, $view, $data);
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ class Activity extends Eloquent
|
||||
{
|
||||
$userName = Auth::check() ? Auth::user()->getFullName() : '<i>System</i>';
|
||||
|
||||
if ($invoice->isRecurring()) {
|
||||
if ($invoice->is_recurring) {
|
||||
$message = $userName . ' created ' . link_to('invoices/'.$invoice->public_id, 'recuring invoice');
|
||||
} else {
|
||||
$message = $userName . ' created invoice ' . link_to('invoices/'.$invoice->public_id, $invoice->invoice_number);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
class Invitation extends EntityModel
|
||||
{
|
||||
protected $hidden = array('id', 'created_at', 'updated_at', 'deleted_at', 'viewed_date');
|
||||
protected $hidden = array('id', 'account_id', 'user_id', 'contact_id', 'created_at', 'updated_at', 'deleted_at', 'viewed_date');
|
||||
|
||||
public function invoice()
|
||||
{
|
||||
|
@ -24,6 +24,11 @@ class Invoice extends EntityModel
|
||||
return $this->belongsTo('InvoiceStatus');
|
||||
}
|
||||
|
||||
public function invitations()
|
||||
{
|
||||
return $this->hasMany('Invitation');
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->invoice_number;
|
||||
@ -34,11 +39,6 @@ class Invoice extends EntityModel
|
||||
return ENTITY_INVOICE;
|
||||
}
|
||||
|
||||
public function isRecurring()
|
||||
{
|
||||
return $this->frequency_id > 0;
|
||||
}
|
||||
|
||||
public function isSent()
|
||||
{
|
||||
return $this->invoice_status_id >= INVOICE_STATUS_SENT;
|
||||
|
@ -207,6 +207,10 @@
|
||||
cursor: move !important;
|
||||
}
|
||||
|
||||
.closer-row {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Animate col width changes */
|
||||
.row div {
|
||||
|
@ -15,61 +15,62 @@
|
||||
'client' => 'required',
|
||||
'product_key' => 'max:14',
|
||||
)); }}
|
||||
|
||||
<!-- <h3>{{ $title }} Invoice</h3> -->
|
||||
|
||||
@if ($invoice)
|
||||
{{ Former::populate($invoice); }}
|
||||
{{ Former::populateField('id', $invoice->public_id); }}
|
||||
{{ Former::populateField('invoice_date', Utils::fromSqlDate($invoice->invoice_date)); }}
|
||||
{{ Former::populateField('due_date', Utils::fromSqlDate($invoice->due_date)); }}
|
||||
{{ Former::populateField('start_date', Utils::fromSqlDate($invoice->start_date)); }}
|
||||
{{ Former::populateField('end_date', Utils::fromSqlDate($invoice->end_date)); }}
|
||||
@else
|
||||
{{ Former::populateField('invoice_number', $invoiceNumber) }}
|
||||
{{ Former::populateField('invoice_date', date('m/d/Y')) }}
|
||||
{{ Former::populateField('start_date', date('m/d/Y')) }}
|
||||
{{ Former::populateField('frequency', FREQUENCY_MONTHLY) }}
|
||||
@endif
|
||||
|
||||
<div class="row" style="min-height:195px">
|
||||
<div class="row" style="min-height:195px" onkeypress="formEnterClick(event)">
|
||||
<div class="col-md-7" id="col_1">
|
||||
{{ Former::select('client')->addOption('', '')->fromQuery($clients, 'name', 'public_id')->select($client ? $client->public_id : '')->addGroupClass('client_select')
|
||||
->help('<a style="cursor:pointer" data-toggle="modal" id="modalLink" onclick="showCreateNew()">Create new client</a>') }}
|
||||
{{ Former::select('client')->addOption('', '')->fromQuery($clients, 'name', 'public_id')->data_bind("dropdown: client")
|
||||
->addGroupClass('client_select closer-row') }}
|
||||
|
||||
<div class="form-group" style="margin-bottom: 8px">
|
||||
<div class="col-lg-8 col-lg-offset-4">
|
||||
<a href="#" data-bind="click: showClientForm, text: showClientText"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-bind="with: client">
|
||||
<div class="form-group" data-bind="visible: contacts().length > 1, foreach: contacts">
|
||||
<div class="col-lg-8 col-lg-offset-4">
|
||||
<label for="test" class="checkbox" data-bind="attr: {for: $index() + '_check'}">
|
||||
<input type="checkbox" value="1" data-bind="checked: send_invoice, attr: {id: $index() + '_check'}">
|
||||
<span data-bind="text: fullName"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::textarea('notes') }}
|
||||
{{ Former::textarea('notes')->data_bind("value: notes, valueUpdate: 'afterkeydown'") }}
|
||||
|
||||
</div>
|
||||
<div class="col-md-4" id="col_2">
|
||||
<div id="recurring_checkbox">
|
||||
{{ Former::checkbox('recurring')->text('Enable | <a href="#">Learn more</a>')->onchange('toggleRecurring()')
|
||||
<div data-bind="visible: invoice_status_id() < CONSTS.INVOICE_STATUS_SENT">
|
||||
{{ Former::checkbox('recurring')->text('Enable | <a href="#">Learn more</a>')->data_bind("checked: is_recurring")
|
||||
->inlineHelp($invoice && $invoice->last_sent_date ? 'Last invoice sent ' . Utils::timestampToDateString($invoice->last_sent_date) : '') }}
|
||||
</div>
|
||||
<div id="recurring_off">
|
||||
{{ Former::text('invoice_number')->label('Invoice #') }}
|
||||
{{ Former::text('po_number')->label('PO number') }}
|
||||
{{ Former::text('invoice_date') }}
|
||||
{{ Former::text('due_date') }}
|
||||
|
||||
|
||||
@if ($invoice && $invoice->recurring_invoice_id)
|
||||
<div style="padding-top: 6px">
|
||||
Created by a {{ link_to('/invoices/'.$invoice->recurring_invoice_id, 'recurring invoice') }}
|
||||
</div>
|
||||
@endif
|
||||
<div data-bind="visible: !is_recurring()">
|
||||
{{ Former::text('invoice_number')->label('Invoice #')->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('po_number')->label('PO number')->data_bind("value: po_number, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('invoice_date')->data_bind("value: invoice_date, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('due_date')->data_bind("value: due_date, valueUpdate: 'afterkeydown'") }}
|
||||
|
||||
{{-- Former::text('invoice_date')->label('Invoice Date')->data_date_format('yyyy-mm-dd') --}}
|
||||
</div>
|
||||
<div id="recurring_on" style="display:none">
|
||||
{{ Former::select('frequency')->label('How often')->options($frequencies)->onchange('updateRecurringStats()') }}
|
||||
{{ Former::text('start_date')->onchange('updateRecurringStats()') }}
|
||||
{{ Former::text('end_date')->onchange('updateRecurringStats()') }}
|
||||
<div data-bind="visible: is_recurring">
|
||||
{{ Former::select('frequency_id')->label('How often')->options($frequencies)->data_bind("value: frequency_id") }}
|
||||
{{ Former::text('start_date')->data_bind("value: start_date, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('end_date')->data_bind("value: end_date, valueUpdate: 'afterkeydown'") }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-3" id="col_3" style="display:none">
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
|
||||
{{ Former::hidden('items')->data_bind("value: ko.toJSON(items)") }}
|
||||
{{ Former::hidden('data')->data_bind("value: ko.toJSON(model)") }}
|
||||
|
||||
<table class="table invoice-table" style="margin-bottom: 0px !important">
|
||||
<thead>
|
||||
@ -77,16 +78,16 @@
|
||||
<th class="hide-border"></th>
|
||||
<th>Item</th>
|
||||
<th>Description</th>
|
||||
<th>Rate</th>
|
||||
<th>Units</th>
|
||||
<th>Unit Cost</th>
|
||||
<th>Quantity</th>
|
||||
<th>Line Total</th>
|
||||
<th class="hide-border"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="sortable: { data: items, afterMove: onDragged }">
|
||||
<tbody data-bind="sortable: { data: invoice_items, afterMove: onDragged }">
|
||||
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }" class="sortable-row">
|
||||
<td style="width:20px;" class="hide-border td-icon">
|
||||
<i data-bind="visible: actionsVisible() && $parent.items().length > 1" class="fa fa-sort"></i>
|
||||
<i data-bind="visible: actionsVisible() && $parent.invoice_items().length > 1" class="fa fa-sort"></i>
|
||||
</td>
|
||||
<td style="width:120px">
|
||||
{{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'key')->onkeyup('onChange()')
|
||||
@ -110,7 +111,7 @@
|
||||
<span data-bind="text: total"></span>
|
||||
</td>
|
||||
<td style="width:20px; cursor:pointer" class="hide-border td-icon">
|
||||
<i data-bind="click: $parent.removeItem, visible: actionsVisible() && $parent.items().length > 1" class="fa fa-minus-circle" title="Remove item"/>
|
||||
<i data-bind="click: $parent.removeItem, visible: actionsVisible() && $parent.invoice_items().length > 1" class="fa fa-minus-circle" title="Remove item"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -189,55 +190,63 @@
|
||||
<h4 class="modal-title" id="myModalLabel">New Client</h4>
|
||||
</div>
|
||||
|
||||
<div class="row" style="padding-left:16px;padding-right:16px" onkeypress="preventFormSubmit(event)">
|
||||
<div class="col-md-6">
|
||||
|
||||
{{ Former::legend('Organization') }}
|
||||
{{ Former::text('name') }}
|
||||
{{ Former::text('work_phone')->label('Phone') }}
|
||||
|
||||
{{ Former::legend('Contact') }}
|
||||
{{ Former::text('first_name') }}
|
||||
{{ Former::text('last_name') }}
|
||||
{{ Former::text('email') }}
|
||||
{{ Former::text('phone') }}
|
||||
<div class="row" data-bind="with: client" style="padding-left:16px;padding-right:16px" onkeypress="modalEnterClick(event)">
|
||||
<div class="col-md-6">
|
||||
|
||||
{{ Former::legend('Organization') }}
|
||||
{{ Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('work_phone')->data_bind("value: work_phone, valueUpdate: 'afterkeydown'")->label('Phone') }}
|
||||
|
||||
|
||||
{{ Former::legend('Address') }}
|
||||
{{ Former::text('address1')->label('Street')->data_bind("value: address1, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('address2')->label('Apt/Floor')->data_bind("value: address2, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('city')->data_bind("value: city, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('state')->data_bind("value: state, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('postal_code')->data_bind("value: postal_code, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::select('country_id')->addOption('','')->label('Country')
|
||||
->fromQuery($countries, 'name', 'id')->data_bind("value: country_id") }}
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{{ 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')->addGroupClass('country_select')
|
||||
->fromQuery($countries, 'name', 'id')->select($client ? $client->country_id : '') }}
|
||||
<div class="col-md-6">
|
||||
|
||||
{{ Former::legend('Contacts') }}
|
||||
<div data-bind='template: { foreach: contacts,
|
||||
beforeRemove: hideContact,
|
||||
afterAdd: showContact }'>
|
||||
{{ 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'") }}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-lg-8 col-lg-offset-4">
|
||||
<span data-bind="visible: $parent.contacts().length > 1">
|
||||
{{ link_to('#', 'Remove contact', array('data-bind'=>'click: $parent.removeContact')) }}
|
||||
</span>
|
||||
<span data-bind="visible: $index() === ($parent.contacts().length - 1)" class="pull-right">
|
||||
{{ link_to('#', 'Add contact', array('data-bind'=>'click: $parent.addContact')) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ Former::legend('Additional Info') }}
|
||||
{{ Former::select('client_size_id')->addOption('','')->label('Size')
|
||||
->fromQuery($clientSizes, 'name', 'id')->select($client ? $client->client_size_id : '') }}
|
||||
{{ Former::select('client_industry_id')->addOption('','')->label('Industry')
|
||||
->fromQuery($clientIndustries, 'name', 'id')->select($client ? $client->client_industry_id : '') }}
|
||||
{{ Former::textarea('notes') }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="modal-body" style="min-height:80px">
|
||||
<div class="form-group">
|
||||
<label for="name" class="control-label col-lg-2 col-sm-4">Name<sup>*</sup></label>
|
||||
<div class="col-lg-10 col-sm-8">
|
||||
<input class="form-control" id="client_name" type="text" name="client_name" onkeypress="nameKeyPress(event)">
|
||||
<span class="help-block" id="nameHelp" style="display: none">Please provide a value</span><span> </span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email" class="control-label col-lg-2 col-sm-4">Email<sup>*</sup></label>
|
||||
<div class="col-lg-10 col-sm-8">
|
||||
<input class="form-control" id="client_email" type="text" name="client_email" onkeypress="nameKeyPress(event)">
|
||||
<span class="help-block" id="emailHelp" style="display: none">Please provide a value</span><span> </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="modal-footer">
|
||||
<span class="error-block" id="nameError" style="display:none;float:left">Please provide a value for the name field.</span><span> </span>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" onclick="newClient()">Done</button>
|
||||
<button type="button" class="btn btn-primary" data-bind="click: clientFormComplete">Done</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -247,17 +256,6 @@
|
||||
{{ Former::close() }}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
/*
|
||||
function textarea_height(TextArea, MaxHeight) {
|
||||
textarea = document.getElementById(TextArea);
|
||||
textareaRows = textarea.value.split("\n");
|
||||
if(textareaRows[0] != "undefined" && textareaRows.length < MaxHeight) counter = textareaRows.length;
|
||||
else if(textareaRows.length >= MaxHeight) counter = MaxHeight;
|
||||
else counter = 1;
|
||||
textarea.rows = counter;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
$(function() {
|
||||
@ -280,13 +278,13 @@
|
||||
$input.combobox();
|
||||
$('.client_select input.form-control').on('change', function(e) {
|
||||
var clientId = parseInt($('input[name=client]').val(), 10);
|
||||
$('#modalLink').text(clientId ? 'Edit client details' : 'Create new client');
|
||||
//$('#modalLink').text(clientId ? 'Edit client details' : 'Create new client');
|
||||
if (clientId > 0) {
|
||||
loadClientDetails(clientId);
|
||||
ko.mapping.fromJS(clientMap[clientId], model.client.mapping, model.client);
|
||||
} else {
|
||||
model.client.public_id(0);
|
||||
}
|
||||
}).trigger('change');
|
||||
|
||||
//enableHoverClick($('.combobox-container input.form-control'), $('.combobox-container input[name=client]'), '{{ URL::to('clients') }}');
|
||||
}).trigger('change');
|
||||
|
||||
@if ($client)
|
||||
$('#invoice_number').focus();
|
||||
@ -304,62 +302,16 @@
|
||||
$('#name').focus();
|
||||
})
|
||||
|
||||
$('#invoice_number').change(refreshPDF);
|
||||
|
||||
$('#actionDropDown > button:first').click(function() {
|
||||
onSaveClick();
|
||||
});
|
||||
|
||||
|
||||
$('label.radio').addClass('radio-inline');
|
||||
|
||||
|
||||
@if ($invoice && $invoice->isRecurring())
|
||||
$('#recurring').prop('checked', true);
|
||||
@elseif (isset($invoice->recurring_invoice_id) && $invoice->recurring_invoice_id)
|
||||
$('#recurring_checkbox > div > div').html('Created by a {{ link_to('/invoices/'.$invoice->recurring_invoice_id, 'recurring invoice') }}').css('padding-top','6px');
|
||||
@elseif ($invoice && $invoice->isSent())
|
||||
$('#recurring_checkbox').hide();
|
||||
@endif
|
||||
|
||||
toggleRecurring();
|
||||
|
||||
applyComboboxListeners();
|
||||
refreshPDF();
|
||||
});
|
||||
|
||||
function loadClientDetails(clientId) {
|
||||
var client = clientMap[clientId];
|
||||
$('#name').val(client.name);
|
||||
$('#work_phone').val(client.work_phone);
|
||||
$('#address1').val(client.address1);
|
||||
$('#address2').val(client.address2);
|
||||
$('#city').val(client.city);
|
||||
$('#state').val(client.state);
|
||||
$('#postal_code').val(client.postal_code);
|
||||
$('#country_id').val(client.country_id).combobox('refresh');
|
||||
for (var i=0; i<client.contacts.length; i++) {
|
||||
var contact = client.contacts[i];
|
||||
if (contact.is_primary) {
|
||||
$('#first_name').val(contact.first_name);
|
||||
$('#last_name').val(contact.last_name);
|
||||
$('#email').val(contact.email);
|
||||
$('#phone').val(contact.phone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showCreateNew() {
|
||||
if (!$('input[name=client]').val()) {
|
||||
$('#myModal input').val('');
|
||||
$('#myModal #country_id').val('');
|
||||
$('#nameError').css( "display", "none" );
|
||||
}
|
||||
|
||||
$('#myModal').modal('show');
|
||||
}
|
||||
|
||||
|
||||
function applyComboboxListeners() {
|
||||
var value;
|
||||
$('.datalist').on('focus', function() {
|
||||
@ -381,78 +333,13 @@
|
||||
});
|
||||
}
|
||||
|
||||
function runCode() {
|
||||
var text = $('#pdfText').val();
|
||||
eval(text);
|
||||
}
|
||||
|
||||
function createInvoiceModel() {
|
||||
var invoice = {
|
||||
invoice_number: $('#invoice_number').val(),
|
||||
invoice_date: $('#invoice_date').val(),
|
||||
discount: parseFloat($('#discount').val()),
|
||||
po_number: $('#po_number').val(),
|
||||
account: {
|
||||
name: "{{ $account->name }}",
|
||||
address1: "{{ $account->address1 }}",
|
||||
address2: "{{ $account->address2 }}",
|
||||
city: "{{ $account->city }}",
|
||||
state: "{{ $account->state }}",
|
||||
postal_code: "{{ $account->postal_code }}",
|
||||
country: {
|
||||
name: "{{ $account->country ? $account->country->name : '' }}"
|
||||
}
|
||||
},
|
||||
@if (file_exists($account->getLogoPath()))
|
||||
image: "{{ HTML::image_data($account->getLogoPath()) }}",
|
||||
imageWidth: {{ $account->getLogoWidth() }},
|
||||
imageHeight: {{ $account->getLogoHeight() }},
|
||||
@endif
|
||||
invoice_items: []
|
||||
};
|
||||
|
||||
var client = {
|
||||
name: $('#name').val(),
|
||||
address1: $('#address1').val(),
|
||||
address2: $('#address2').val(),
|
||||
city: $('#city').val(),
|
||||
state: $('#state').val(),
|
||||
postal_code: $('#postal_code').val(),
|
||||
country: {
|
||||
name: $('.country_select input[type=text]').val()
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
var clientId = $('input[name=client]').val();
|
||||
if (clientId == '-1') {
|
||||
var client = {
|
||||
name: $('#name').val(),
|
||||
address1: $('#address1').val(),
|
||||
address2: $('#address2').val(),
|
||||
city: $('#city').val(),
|
||||
state: $('#state').val(),
|
||||
postal_code: $('#postal_code').val(),
|
||||
country: {
|
||||
name: $('.country_select input[type=text]').val()
|
||||
}
|
||||
};
|
||||
} else if (clientMap.hasOwnProperty(clientId)) {
|
||||
var client = clientMap[clientId];
|
||||
}
|
||||
*/
|
||||
invoice.client = client;
|
||||
|
||||
for(var i=0; i<model.items().length; i++) {
|
||||
var item = model.items()[i];
|
||||
invoice.invoice_items.push({
|
||||
product_key: item.product_key(),
|
||||
notes: item.notes(),
|
||||
cost: item.cost(),
|
||||
qty: item.qty()
|
||||
});
|
||||
}
|
||||
|
||||
var invoice = ko.toJS(model);
|
||||
@if (file_exists($account->getLogoPath()))
|
||||
invoice.image = "{{ HTML::image_data($account->getLogoPath()) }}";
|
||||
invoice.imageWidth = {{ $account->getLogoWidth() }};
|
||||
invoice.imageHeight = {{ $account->getLogoHeight() }};
|
||||
@endif
|
||||
return invoice;
|
||||
}
|
||||
|
||||
@ -464,6 +351,7 @@
|
||||
|
||||
|
||||
function _refreshPDF() {
|
||||
console.log("refreshPDF");
|
||||
var invoice = createInvoiceModel();
|
||||
var doc = generatePDF(invoice);
|
||||
|
||||
@ -524,81 +412,101 @@
|
||||
}
|
||||
}
|
||||
|
||||
function newClient() {
|
||||
var name = $('#name').val();
|
||||
if (!name) {
|
||||
if (!name) $('#nameError').css( "display", "inline" );
|
||||
} else {
|
||||
$('select#client').combobox('setSelected');
|
||||
if (!$('input[name=client]').val()) {
|
||||
$('input[name=client]').val('-1');
|
||||
function formEnterClick(event) {
|
||||
if (event.keyCode === 13){
|
||||
if (event.target.type == 'textarea') {
|
||||
return;
|
||||
}
|
||||
$('.client_select input.form-control').val(name);
|
||||
$('.client_select .combobox-container').addClass('combobox-selected');
|
||||
|
||||
$('#nameError').css( "display", "none" );
|
||||
$('#modalLink').text('Edit client details');
|
||||
$('#myModal').modal('hide');
|
||||
$('.client_select input.form-control').focus();
|
||||
|
||||
refreshPDF();
|
||||
event.preventDefault();
|
||||
$('.main_form').submit();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function preventFormSubmit(event) {
|
||||
function modalEnterClick(event) {
|
||||
if (event.keyCode === 13){
|
||||
event.preventDefault();
|
||||
newClient();
|
||||
model.clientFormComplete();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function InvoiceModel() {
|
||||
var self = this;
|
||||
self.discount = ko.observable();
|
||||
self.items = ko.observableArray();
|
||||
var self = this;
|
||||
this.client = new ClientModel();
|
||||
self.discount = ko.observable('');
|
||||
self.frequency_id = ko.observable('');
|
||||
self.notes = ko.observable('');
|
||||
self.po_number = ko.observable('');
|
||||
self.invoice_date = ko.observable('');
|
||||
self.due_date = ko.observable('');
|
||||
self.start_date = ko.observable('');
|
||||
self.end_date = ko.observable('');
|
||||
self.is_recurring = ko.observable(false);
|
||||
self.invoice_status_id = ko.observable(0);
|
||||
self.invoice_items = ko.observableArray();
|
||||
|
||||
@if (isset($items) && $items)
|
||||
@foreach ($items as $item)
|
||||
var item = new ItemModel();
|
||||
item.product_key("{{ $item->product_key }}");
|
||||
item.notes('{{ str_replace("\n", "\\n", $item->notes) }}');
|
||||
item.cost("{{ isset($item->cost) ? $item->cost : '' }}");
|
||||
item.qty("{{ isset($item->qty) ? $item->qty : '' }}");
|
||||
self.items.push(item);
|
||||
@endforeach
|
||||
@elseif ($invoice)
|
||||
self.discount({{ $invoice->discount }});
|
||||
@if (count($invoice->invoice_items) > 0)
|
||||
@foreach ($invoice->invoice_items as $item)
|
||||
var item = new ItemModel();
|
||||
item.product_key("{{ $item->product_key }}");
|
||||
item.notes('{{ str_replace("\n", "\\n", $item->notes) }}');
|
||||
item.cost("{{ $item->cost }}");
|
||||
item.qty("{{ $item->qty }}");
|
||||
self.items.push(item);
|
||||
@endforeach
|
||||
@endif
|
||||
@endif
|
||||
self.mapping = {
|
||||
'invoice_items': {
|
||||
create: function(options) {
|
||||
return new ItemModel(options.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.showClientText = ko.computed(function() {
|
||||
return self.client.public_id() ? 'Edit client details' : 'Create new client';
|
||||
});
|
||||
|
||||
|
||||
self.showClientForm = function() {
|
||||
if (self.client.public_id() == 0) {
|
||||
$('#myModal input').val('');
|
||||
$('#myModal #country_id').val('');
|
||||
}
|
||||
|
||||
$('#nameError').css( "display", "none" );
|
||||
$('#myModal').modal('show');
|
||||
}
|
||||
|
||||
self.clientFormComplete = function() {
|
||||
var name = $('#name').val();
|
||||
if (!name) {
|
||||
if (!name) $('#nameError').css( "display", "inline" );
|
||||
return;
|
||||
}
|
||||
|
||||
$('select#client').combobox('setSelected');
|
||||
if (self.client.public_id() == 0) {
|
||||
self.client.public_id(-1);
|
||||
}
|
||||
$('.client_select input.form-control').val(name);
|
||||
$('.client_select .combobox-container').addClass('combobox-selected');
|
||||
|
||||
$('#nameError').css( "display", "none" );
|
||||
$('.client_select input.form-control').focus();
|
||||
|
||||
refreshPDF();
|
||||
|
||||
$('#myModal').modal('hide');
|
||||
}
|
||||
|
||||
self.items.push(new ItemModel());
|
||||
|
||||
self.removeItem = function(item) {
|
||||
self.items.remove(item);
|
||||
self.invoice_items.remove(item);
|
||||
refreshPDF();
|
||||
}
|
||||
|
||||
self.addItem = function() {
|
||||
self.items.push(new ItemModel());
|
||||
self.invoice_items.push(new ItemModel());
|
||||
applyComboboxListeners();
|
||||
}
|
||||
|
||||
this.rawSubtotal = ko.computed(function() {
|
||||
var total = 0;
|
||||
for(var p = 0; p < self.items().length; ++p)
|
||||
for(var p = 0; p < self.invoice_items().length; ++p)
|
||||
{
|
||||
total += self.items()[p].rawTotal();
|
||||
total += self.invoice_items()[p].rawTotal();
|
||||
}
|
||||
return total;
|
||||
});
|
||||
@ -630,8 +538,74 @@
|
||||
}
|
||||
}
|
||||
|
||||
function ItemModel() {
|
||||
function ClientModel(data) {
|
||||
var self = this;
|
||||
self.public_id = ko.observable(0);
|
||||
self.name = ko.observable('');
|
||||
self.work_phone = ko.observable('');
|
||||
self.notes = ko.observable('');
|
||||
self.address1 = ko.observable('');
|
||||
self.address2 = ko.observable('');
|
||||
self.city = ko.observable('');
|
||||
self.state = ko.observable('');
|
||||
self.postal_code = ko.observable('');
|
||||
self.country_id = ko.observable('');
|
||||
self.client_size_id = ko.observable('');
|
||||
self.client_industry_id = ko.observable('');
|
||||
self.contacts = ko.observableArray();
|
||||
|
||||
self.mapping = {
|
||||
'contacts': {
|
||||
create: function(options) {
|
||||
return new ContactModel(options.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.showContact = function(elem) { if (elem.nodeType === 1) $(elem).hide().slideDown() }
|
||||
self.hideContact = function(elem) { if (elem.nodeType === 1) $(elem).slideUp(function() { $(elem).remove(); }) }
|
||||
|
||||
self.addContact = function() {
|
||||
var contact = new ContactModel();
|
||||
console.log('num: ' + self.contacts().length);
|
||||
if (self.contacts().length == 0) {
|
||||
contact.send_invoice(true);
|
||||
}
|
||||
self.contacts.push(contact);
|
||||
return false;
|
||||
}
|
||||
|
||||
self.removeContact = function() {
|
||||
self.contacts.remove(this);
|
||||
}
|
||||
|
||||
if (data) {
|
||||
ko.mapping.fromJS(data, {}, this);
|
||||
} else {
|
||||
self.addContact();
|
||||
}
|
||||
}
|
||||
|
||||
function ContactModel(data) {
|
||||
var self = this;
|
||||
self.public_id = ko.observable('');
|
||||
self.first_name = ko.observable('');
|
||||
self.last_name = ko.observable('');
|
||||
self.email = ko.observable('');
|
||||
self.phone = ko.observable('');
|
||||
self.send_invoice = ko.observable(false);
|
||||
|
||||
if (data) {
|
||||
ko.mapping.fromJS(data, {}, this);
|
||||
}
|
||||
|
||||
self.fullName = ko.computed(function() {
|
||||
return self.first_name() + ' ' + self.last_name();
|
||||
});
|
||||
}
|
||||
|
||||
function ItemModel(data) {
|
||||
var self = this;
|
||||
this.product_key = ko.observable('');
|
||||
this.notes = ko.observable('');
|
||||
this.cost = ko.observable();
|
||||
@ -639,6 +613,10 @@
|
||||
this.tax = ko.observable();
|
||||
this.actionsVisible = ko.observable(false);
|
||||
|
||||
if (data) {
|
||||
ko.mapping.fromJS(data, {}, this);
|
||||
}
|
||||
|
||||
this.rawTotal = ko.computed(function() {
|
||||
var cost = parseFloat(self.cost());
|
||||
var qty = parseFloat(self.qty());
|
||||
@ -701,8 +679,8 @@
|
||||
function onChange()
|
||||
{
|
||||
var hasEmpty = false;
|
||||
for(var i=0; i<model.items().length; i++) {
|
||||
var item = model.items()[i];
|
||||
for(var i=0; i<model.invoice_items().length; i++) {
|
||||
var item = model.invoice_items()[i];
|
||||
if (item.isEmpty()) {
|
||||
hasEmpty = true;
|
||||
}
|
||||
@ -712,73 +690,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
function toggleRecurring()
|
||||
{
|
||||
var enabled = $('#recurring').is(':checked');
|
||||
|
||||
if (enabled) {
|
||||
$('#recurring_on').show();
|
||||
$('#recurring_off').hide();
|
||||
$('#email_button').prop('disabled', true);
|
||||
} else {
|
||||
$('#recurring_on').hide();
|
||||
$('#recurring_off').show();
|
||||
$('#email_button').prop('disabled', false);
|
||||
}
|
||||
|
||||
/*
|
||||
$('#col_1').toggleClass('col-md-6 col-md-5');
|
||||
$('#col_2').toggleClass('col-md-5 col-md-3');
|
||||
|
||||
if (show) {
|
||||
setTimeout(function() {
|
||||
$('#col_3').show();
|
||||
}, 500);
|
||||
} else {
|
||||
$('#col_3').hide();
|
||||
}
|
||||
|
||||
$('#showRecurring,#hideRecurring').toggle();
|
||||
|
||||
if (!show) {
|
||||
$('#how_often, #start_date, #end_date').val('')
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
function updateRecurringStats()
|
||||
{
|
||||
/*
|
||||
var howOften = $('#how_often').val();
|
||||
var startDate = $('#start_date').val();
|
||||
var endDate = $('#end_date').val();
|
||||
console.log("%s %s %s", howOften, startDate, endDate);
|
||||
|
||||
var str = "Send ";
|
||||
if (!endDate) {
|
||||
str += " an unlimited number of ";
|
||||
} else {
|
||||
str += "";
|
||||
}
|
||||
str += " emails";
|
||||
$('#stats').html(str);
|
||||
*/
|
||||
}
|
||||
|
||||
var products = {{ $products }};
|
||||
var clients = {{ $clients }};
|
||||
var clientMap = {};
|
||||
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
for (var i=0; i<client.contacts.length; i++) {
|
||||
var contact = client.contacts[i];
|
||||
contact.send_invoice = contact.is_primary;
|
||||
}
|
||||
clientMap[client.public_id] = client;
|
||||
}
|
||||
|
||||
window.model = new InvoiceModel();
|
||||
|
||||
window.model = new InvoiceModel();
|
||||
@if ($invoice)
|
||||
var invoice = {{ $invoice }};
|
||||
ko.mapping.fromJS(invoice, model.mapping, model);
|
||||
if (!model.discount()) model.discount('');
|
||||
var invitationContactIds = {{ json_encode($invitationContactIds) }};
|
||||
var client = clientMap[invoice.client.public_id];
|
||||
for (var i=0; i<client.contacts.length; i++) {
|
||||
var contact = client.contacts[i];
|
||||
contact.send_invoice = invitationContactIds.indexOf(contact.public_id) >= 0;
|
||||
}
|
||||
@else
|
||||
model.invoice_number = '{{ $invoiceNumber }}';
|
||||
@endif
|
||||
model.invoice_items.push(new ItemModel());
|
||||
ko.applyBindings(model);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@stop
|
@ -1,6 +1,6 @@
|
||||
function generatePDF(invoice) {
|
||||
var invoiceNumber = invoice.invoice_number;
|
||||
var issuedOn = invoice.invoice_date;
|
||||
var issuedOn = invoice.invoice_date ? invoice.invoice_date : '';
|
||||
var amount = '$0.00';
|
||||
|
||||
var marginLeft = 90;
|
||||
@ -560,4 +560,40 @@ function convertDataURIToBinary(dataURI) {
|
||||
array[i] = raw.charCodeAt(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ko.bindingHandlers.dropdown = {
|
||||
init: function (element, valueAccessor, allBindingsAccessor) {
|
||||
var options = allBindingsAccessor().dropdownOptions|| {};
|
||||
var value = ko.utils.unwrapObservable(valueAccessor());
|
||||
var id = (value && value.public_id) ? value.public_id() : (value && value.id) ? value.id() : false;
|
||||
if (id) $(element).val(id);
|
||||
console.log("combo-init: %s", id);
|
||||
$(element).combobox(options);
|
||||
|
||||
/*
|
||||
ko.utils.registerEventHandler(element, "change", function () {
|
||||
console.log("change: %s", $(element).val());
|
||||
var
|
||||
valueAccessor($(element).val());
|
||||
//$(element).combobox('refresh');
|
||||
});
|
||||
*/
|
||||
},
|
||||
update: function (element, valueAccessor) {
|
||||
var value = ko.utils.unwrapObservable(valueAccessor());
|
||||
var id = (value && value.public_id) ? value.public_id() : (value && value.id) ? value.id() : false;
|
||||
console.log("combo-update: %s", id);
|
||||
if (id) $(element).val(id);
|
||||
$(element).combobox('refresh');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var CONSTS = {};
|
||||
CONSTS.INVOICE_STATUS_DRAFT = 1;
|
||||
CONSTS.INVOICE_STATUS_SENT = 2;
|
||||
CONSTS.INVOICE_STATUS_VIEWED = 3;
|
||||
CONSTS.INVOICE_STATUS_PARTIAL = 4;
|
||||
CONSTS.INVOICE_STATUS_PAID = 5;
|
||||
|
Loading…
x
Reference in New Issue
Block a user