mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-18 09:04:35 +01:00
Bug fixes
This commit is contained in:
parent
aa8151edaf
commit
130a176888
@ -10,6 +10,7 @@ class ConfideSetupUsersTable extends Migration {
|
|||||||
*/
|
*/
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
|
Schema::dropIfExists('tax_rates');
|
||||||
Schema::dropIfExists('themes');
|
Schema::dropIfExists('themes');
|
||||||
Schema::dropIfExists('credits');
|
Schema::dropIfExists('credits');
|
||||||
Schema::dropIfExists('activities');
|
Schema::dropIfExists('activities');
|
||||||
@ -348,6 +349,9 @@ class ConfideSetupUsersTable extends Migration {
|
|||||||
$t->decimal('cost', 10, 2);
|
$t->decimal('cost', 10, 2);
|
||||||
$t->decimal('qty', 10, 2);
|
$t->decimal('qty', 10, 2);
|
||||||
|
|
||||||
|
$t->string('tax_name');
|
||||||
|
$t->decimal('tax_rate', 10, 2);
|
||||||
|
|
||||||
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
||||||
$t->foreign('product_id')->references('id')->on('products');
|
$t->foreign('product_id')->references('id')->on('products');
|
||||||
$t->foreign('user_id')->references('id')->on('users');
|
$t->foreign('user_id')->references('id')->on('users');
|
||||||
@ -408,6 +412,24 @@ class ConfideSetupUsersTable extends Migration {
|
|||||||
$t->unique( array('account_id','public_id') );
|
$t->unique( array('account_id','public_id') );
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Schema::create('tax_rates', function($t)
|
||||||
|
{
|
||||||
|
$t->increments('id');
|
||||||
|
$t->unsignedInteger('account_id')->index();
|
||||||
|
$t->unsignedInteger('user_id');
|
||||||
|
$t->timestamps();
|
||||||
|
$t->softDeletes();
|
||||||
|
|
||||||
|
$t->string('name');
|
||||||
|
$t->decimal('rate', 10, 2);
|
||||||
|
|
||||||
|
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$t->foreign('user_id')->references('id')->on('users');
|
||||||
|
|
||||||
|
$t->unsignedInteger('public_id');
|
||||||
|
$t->unique( array('account_id','public_id') );
|
||||||
|
});
|
||||||
|
|
||||||
Schema::create('activities', function($t)
|
Schema::create('activities', function($t)
|
||||||
{
|
{
|
||||||
$t->increments('id');
|
$t->increments('id');
|
||||||
@ -439,6 +461,7 @@ class ConfideSetupUsersTable extends Migration {
|
|||||||
*/
|
*/
|
||||||
public function down()
|
public function down()
|
||||||
{
|
{
|
||||||
|
Schema::dropIfExists('tax_rates');
|
||||||
Schema::dropIfExists('themes');
|
Schema::dropIfExists('themes');
|
||||||
Schema::dropIfExists('credits');
|
Schema::dropIfExists('credits');
|
||||||
Schema::dropIfExists('activities');
|
Schema::dropIfExists('activities');
|
||||||
|
6
app/models/TaxRate.php
Executable file
6
app/models/TaxRate.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class TaxRate extends EntityModel
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
28
app/ninja/repositories/AccountRepository.php
Normal file → Executable file
28
app/ninja/repositories/AccountRepository.php
Normal file → Executable file
@ -9,35 +9,45 @@ class AccountRepository
|
|||||||
{
|
{
|
||||||
$clients = \DB::table('clients')
|
$clients = \DB::table('clients')
|
||||||
->where('clients.deleted_at', '=', null)
|
->where('clients.deleted_at', '=', null)
|
||||||
->select(\DB::raw("'Clients' as type, clients.public_id, clients.name"));
|
->select(\DB::raw("'Clients' as type, clients.public_id, clients.name, '' as token"));
|
||||||
|
|
||||||
$contacts = \DB::table('clients')
|
$contacts = \DB::table('clients')
|
||||||
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||||
->where('clients.deleted_at', '=', null)
|
->where('clients.deleted_at', '=', null)
|
||||||
->select(\DB::raw("'Contacts' as type, clients.public_id, CONCAT(contacts.first_name, ' ', contacts.last_name) as name"));
|
->select(\DB::raw("'Contacts' as type, clients.public_id, CONCAT(contacts.first_name, ' ', contacts.last_name, ': ', clients.name) as name, '' as token"));
|
||||||
|
|
||||||
$invoices = \DB::table('clients')
|
$invoices = \DB::table('clients')
|
||||||
->join('invoices', 'invoices.client_id', '=', 'clients.id')
|
->join('invoices', 'invoices.client_id', '=', 'clients.id')
|
||||||
->where('clients.deleted_at', '=', null)
|
->where('clients.deleted_at', '=', null)
|
||||||
->where('invoices.deleted_at', '=', null)
|
->where('invoices.deleted_at', '=', null)
|
||||||
->select(\DB::raw("'Invoices' as type, invoices.public_id, CONCAT(invoices.invoice_number, ': ', clients.name) as name"));
|
->select(\DB::raw("'Invoices' as type, invoices.public_id, CONCAT(invoices.invoice_number, ': ', clients.name) as name, invoices.invoice_number as token"));
|
||||||
|
|
||||||
|
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($clients->union($contacts)->union($invoices)->get() as $row)
|
foreach ($clients->union($contacts)->union($invoices)->get() as $row)
|
||||||
{
|
{
|
||||||
if (!isset($data[$row->type]))
|
$type = $row->type;
|
||||||
|
|
||||||
|
if (!isset($data[$type]))
|
||||||
{
|
{
|
||||||
$data[$row->type] = [];
|
$data[$type] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokens = explode(' ', $row->name);
|
||||||
|
$tokens[] = $type;
|
||||||
|
|
||||||
|
if ($type == 'Invoices')
|
||||||
|
{
|
||||||
|
$tokens[] = intVal($row->token) . '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$data[$row->type][] = [
|
$data[$type][] = [
|
||||||
'value' => $row->name,
|
'value' => $row->name,
|
||||||
'public_id' => $row->public_id
|
'public_id' => $row->public_id,
|
||||||
|
'tokens' => $tokens
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -30,7 +30,6 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="{{ asset('css/datepicker.css') }}"/>
|
<link rel="stylesheet" type="text/css" href="{{ asset('css/datepicker.css') }}"/>
|
||||||
|
|
||||||
<script src="{{ asset('js/typeahead.js') }}" type="text/javascript"></script>
|
<script src="{{ asset('js/typeahead.js') }}" type="text/javascript"></script>
|
||||||
<script src="{{ asset('js/hogan-2.0.0.js') }}" type="text/javascript"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="{{ asset('css/typeahead.js-bootstrap.css') }}"/>
|
<link rel="stylesheet" type="text/css" href="{{ asset('css/typeahead.js-bootstrap.css') }}"/>
|
||||||
|
|
||||||
<script src="{{ asset('js/script.js') }}" type="text/javascript"></script>
|
<script src="{{ asset('js/script.js') }}" type="text/javascript"></script>
|
||||||
@ -501,7 +500,7 @@
|
|||||||
if (!data.hasOwnProperty(type)) continue;
|
if (!data.hasOwnProperty(type)) continue;
|
||||||
datasets.push({
|
datasets.push({
|
||||||
name: type,
|
name: type,
|
||||||
header: ' <b>' + type + '</b>',
|
header: ' <b>' + type + '</b>',
|
||||||
local: data[type]
|
local: data[type]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,14 @@
|
|||||||
{{ Former::text('po_number')->label('PO number')->data_bind("value: po_number, valueUpdate: 'afterkeydown'") }}
|
{{ Former::text('po_number')->label('PO number')->data_bind("value: po_number, valueUpdate: 'afterkeydown'") }}
|
||||||
{{ Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
|
{{ Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
|
||||||
{{ Former::text('currency')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
|
{{ Former::text('currency')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
|
||||||
|
|
||||||
|
<div class="form-group" style="margin-bottom: 8px">
|
||||||
|
<label for="recurring" class="control-label col-lg-4 col-sm-4">Taxes</label>
|
||||||
|
<div class="col-lg-8 col-sm-8" style="padding-top: 7px">
|
||||||
|
<a href="#" data-bind="click: showTaxesForm">Manage taxe rates</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -82,6 +90,7 @@
|
|||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
<th>Unit Cost</th>
|
<th>Unit Cost</th>
|
||||||
<th>Quantity</th>
|
<th>Quantity</th>
|
||||||
|
<th data-bind="visible: tax_rates().length > 1">Tax</th>
|
||||||
<th>Line Total</th>
|
<th>Line Total</th>
|
||||||
<th class="hide-border"></th>
|
<th class="hide-border"></th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -92,24 +101,22 @@
|
|||||||
<i data-bind="visible: actionsVisible() && $parent.invoice_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>
|
||||||
<td style="width:120px">
|
<td style="width:120px">
|
||||||
{{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'key')->onkeyup('onChange()')
|
{{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'key')->onkeyup('onItemChange()')
|
||||||
->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }}
|
->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }}
|
||||||
</td>
|
</td>
|
||||||
<td style="width:300px">
|
<td style="width:300px">
|
||||||
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" style="resize: none;" class="form-control word-wrap" onchange="refreshPDF()"></textarea>
|
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" style="resize: none;" class="form-control word-wrap" onchange="refreshPDF()"></textarea>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:100px">
|
<td style="width:100px">
|
||||||
<input onkeyup="onChange()" data-bind="value: cost, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
<input onkeyup="onItemChange()" data-bind="value: cost, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:80px">
|
<td style="width:80px">
|
||||||
<input onkeyup="onChange()" data-bind="value: qty, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
<input onkeyup="onItemChange()" data-bind="value: qty, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
||||||
</td>
|
</td>
|
||||||
<!--
|
<td style="width:80px; vertical-align:middle" data-bind="visible: $parent.tax_rates().length > 1">
|
||||||
<td style="width:100px">
|
<select style="width:100%" data-bind="options: $parent.tax_rates"></select>
|
||||||
<input data-bind="value: tax, valueUpdate: 'afterkeydown'"/>
|
|
||||||
</td>
|
</td>
|
||||||
-->
|
<td style="width:100px;text-align: right;padding-top:9px !important">
|
||||||
<td style="width:100px;text-align: right;padding-top:9px !important">
|
|
||||||
<span data-bind="text: total"></span>
|
<span data-bind="text: total"></span>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:20px; cursor:pointer" class="hide-border td-icon">
|
<td style="width:20px; cursor:pointer" class="hide-border td-icon">
|
||||||
@ -119,26 +126,22 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="hide-border"></td>
|
<td class="hide-border" data-bind="attr: {colspan: tax_rates().length > 1 ? 4 : 3}"/>
|
||||||
<td colspan="2"/>
|
|
||||||
<td colspan="2">Subtotal</td>
|
<td colspan="2">Subtotal</td>
|
||||||
<td style="text-align: right"><span data-bind="text: subtotal"/></td>
|
<td style="text-align: right"><span data-bind="text: subtotal"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="hide-border"></td>
|
<td class="hide-border" data-bind="attr: {colspan: tax_rates().length > 1 ? 4 : 3}"/>
|
||||||
<td colspan="2" class="hide-border"/>
|
|
||||||
<td colspan="2">Paid to Date</td>
|
<td colspan="2">Paid to Date</td>
|
||||||
<td style="text-align: right"></td>
|
<td style="text-align: right"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr data-bind="visible: discount() > 0">
|
<tr data-bind="visible: discount() > 0">
|
||||||
<td class="hide-border"></td>
|
<td class="hide-border" data-bind="attr: {colspan: tax_rates().length > 1 ? 4 : 3}"/>
|
||||||
<td colspan="2" class="hide-border"/>
|
|
||||||
<td colspan="2">Discount</td>
|
<td colspan="2">Discount</td>
|
||||||
<td style="text-align: right"><span data-bind="text: discounted"/></td>
|
<td style="text-align: right"><span data-bind="text: discounted"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="hide-border"></td>
|
<td class="hide-border" data-bind="attr: {colspan: tax_rates().length > 1 ? 4 : 3}"/>
|
||||||
<td colspan="2" class="hide-border"/>
|
|
||||||
<td colspan="2"><b>Balance Due</b></td>
|
<td colspan="2"><b>Balance Due</b></td>
|
||||||
<td style="text-align: right"><span data-bind="text: total"/></td>
|
<td style="text-align: right"><span data-bind="text: total"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -184,16 +187,17 @@
|
|||||||
<canvas id="theCanvas" style="display:none;width:100%;border:solid 1px #CCCCCC;"></canvas>
|
<canvas id="theCanvas" style="display:none;width:100%;border:solid 1px #CCCCCC;"></canvas>
|
||||||
|
|
||||||
|
|
||||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade" id="clientModal" tabindex="-1" role="dialog" aria-labelledby="clientModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog" style="min-width:1000px">
|
<div class="modal-dialog" style="min-width:1000px">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<h4 class="modal-title" id="myModalLabel">New Client</h4>
|
<h4 class="modal-title" id="clientModalLabel">New Client</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" data-bind="with: client" style="padding-left:16px;padding-right:16px" onkeypress="modalEnterClick(event)">
|
<div class="container" style="width: 100%">
|
||||||
<div class="col-md-6">
|
<div style="background-color: #F6F6F6" class="row" data-bind="with: client" onkeypress="clientModalEnterClick(event)">
|
||||||
|
<div class="col-md-6" style="margin-left:0px;margin-right:0px" >
|
||||||
|
|
||||||
{{ Former::legend('Organization') }}
|
{{ Former::legend('Organization') }}
|
||||||
{{ Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown'") }}
|
{{ Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown'") }}
|
||||||
@ -211,7 +215,7 @@
|
|||||||
->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") }}
|
->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6" style="margin-left:0px;margin-right:0px" >
|
||||||
|
|
||||||
{{ Former::legend('Contacts') }}
|
{{ Former::legend('Contacts') }}
|
||||||
<div data-bind='template: { foreach: contacts,
|
<div data-bind='template: { foreach: contacts,
|
||||||
@ -244,18 +248,64 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer" style="margin-top: 0px">
|
||||||
<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>
|
<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-default" data-dismiss="modal">Cancel</button>
|
||||||
<button type="button" class="btn btn-primary" data-bind="click: clientFormComplete">Done</button>
|
<button type="button" class="btn btn-primary" data-bind="click: clientFormComplete">Done</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="taxModal" tabindex="-1" role="dialog" aria-labelledby="taxModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" style="min-width:150px">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title" id="taxModalLabel">Tax Rates</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="background-color: #F6F6F6" onkeypress="taxModalEnterClick(event)">
|
||||||
|
<table class="table invoice-table sides-padded" style="margin-bottom: 0px !important">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="hide-border"></th>
|
||||||
|
<th class="hide-border">Name</th>
|
||||||
|
<th class="hide-border">Rate</th>
|
||||||
|
<th class="hide-border"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody data-bind="foreach: tax_rates">
|
||||||
|
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }">
|
||||||
|
<td style="width:10px" class="hide-border"></td>
|
||||||
|
<td style="width:60px">
|
||||||
|
<input onkeyup="onTaxRateChange()" data-bind="value: name, valueUpdate: 'afterkeydown'" class="form-control" onchange="refreshPDF()"//>
|
||||||
|
</td>
|
||||||
|
<td style="width:60px">
|
||||||
|
<input onkeyup="onTaxRateChange()" data-bind="value: rate, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
||||||
|
</td>
|
||||||
|
<td style="width:10px; cursor:pointer" class="hide-border td-icon">
|
||||||
|
<i data-bind="click: $parent.removeTaxRate, visible: actionsVisible() && $parent.tax_rates().length > 1" class="fa fa-minus-circle" title="Remove item"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer" style="margin-top: 0px">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" data-bind="click: taxFormComplete">Done</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{{ Former::close() }}
|
{{ Former::close() }}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@ -302,7 +352,7 @@
|
|||||||
//$('[name="client_combobox"]').focus();
|
//$('[name="client_combobox"]').focus();
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
$('#myModal').on('hidden.bs.modal', function () {
|
$('#clientModal').on('hidden.bs.modal', function () {
|
||||||
if (model.clientBackup) {
|
if (model.clientBackup) {
|
||||||
console.log("Loading backup");
|
console.log("Loading backup");
|
||||||
//console.log(model.clientBackup);
|
//console.log(model.clientBackup);
|
||||||
@ -312,10 +362,14 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
$('#myModal').on('shown.bs.modal', function () {
|
$('#clientModal').on('shown.bs.modal', function () {
|
||||||
$('#name').focus();
|
$('#name').focus();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$('#taxModal').on('shown.bs.modal', function () {
|
||||||
|
$('#taxModal input:first').focus();
|
||||||
|
})
|
||||||
|
|
||||||
$('#actionDropDown > button:first').click(function() {
|
$('#actionDropDown > button:first').click(function() {
|
||||||
onSaveClick();
|
onSaveClick();
|
||||||
});
|
});
|
||||||
@ -438,7 +492,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function modalEnterClick(event) {
|
function clientModalEnterClick(event) {
|
||||||
if (event.keyCode === 13){
|
if (event.keyCode === 13){
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
model.clientFormComplete();
|
model.clientFormComplete();
|
||||||
@ -446,6 +500,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function taxModalEnterClick(event) {
|
||||||
|
if (event.keyCode === 13){
|
||||||
|
event.preventDefault();
|
||||||
|
model.taxFormComplete();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function InvoiceModel() {
|
function InvoiceModel() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.client = new ClientModel();
|
this.client = new ClientModel();
|
||||||
@ -460,7 +522,9 @@
|
|||||||
self.end_date = ko.observable('');
|
self.end_date = ko.observable('');
|
||||||
self.is_recurring = ko.observable(false);
|
self.is_recurring = ko.observable(false);
|
||||||
self.invoice_status_id = ko.observable(0);
|
self.invoice_status_id = ko.observable(0);
|
||||||
|
|
||||||
self.invoice_items = ko.observableArray();
|
self.invoice_items = ko.observableArray();
|
||||||
|
self.tax_rates = ko.observableArray();
|
||||||
|
|
||||||
self.mapping = {
|
self.mapping = {
|
||||||
'invoice_items': {
|
'invoice_items': {
|
||||||
@ -491,17 +555,27 @@
|
|||||||
return self.client.public_id() ? 'Edit client details' : 'Create new client';
|
return self.client.public_id() ? 'Edit client details' : 'Create new client';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
self.showTaxesForm = function() {
|
||||||
|
$('#taxModal').modal('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
self.taxFormComplete = function() {
|
||||||
|
$('#taxModal').modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
self.showClientForm = function() {
|
self.showClientForm = function() {
|
||||||
self.clientBackup = ko.mapping.toJS(self.client);
|
self.clientBackup = ko.mapping.toJS(self.client);
|
||||||
console.log(self.clientBackup);
|
//console.log(self.clientBackup);
|
||||||
|
|
||||||
if (self.client.public_id() == 0) {
|
if (self.client.public_id() == 0) {
|
||||||
$('#myModal input').val('');
|
$('#clientModal input').val('');
|
||||||
$('#myModal #country_id').val('');
|
$('#clientModal #country_id').val('');
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#nameError').css( "display", "none" );
|
$('#nameError').css( "display", "none" );
|
||||||
$('#myModal').modal('show');
|
$('#clientModal').modal('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
self.clientFormComplete = function() {
|
self.clientFormComplete = function() {
|
||||||
@ -523,7 +597,7 @@
|
|||||||
|
|
||||||
refreshPDF();
|
refreshPDF();
|
||||||
model.clientBackup = false;
|
model.clientBackup = false;
|
||||||
$('#myModal').modal('hide');
|
$('#clientModal').modal('hide');
|
||||||
}
|
}
|
||||||
|
|
||||||
self.removeItem = function(item) {
|
self.removeItem = function(item) {
|
||||||
@ -536,6 +610,16 @@
|
|||||||
applyComboboxListeners();
|
applyComboboxListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.removeTaxRate = function(taxRate) {
|
||||||
|
self.tax_rates.remove(taxRate);
|
||||||
|
//refreshPDF();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addTaxRate = function() {
|
||||||
|
self.tax_rates.push(new TaxRateModel());
|
||||||
|
applyComboboxListeners();
|
||||||
|
}
|
||||||
|
|
||||||
this.rawSubtotal = ko.computed(function() {
|
this.rawSubtotal = ko.computed(function() {
|
||||||
var total = 0;
|
var total = 0;
|
||||||
for(var p = 0; p < self.invoice_items().length; ++p)
|
for(var p = 0; p < self.invoice_items().length; ++p)
|
||||||
@ -639,13 +723,33 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TaxRateModel(data) {
|
||||||
|
var self = this;
|
||||||
|
this.rate = ko.observable();
|
||||||
|
this.name = ko.observable('');
|
||||||
|
this.actionsVisible = ko.observable(false);
|
||||||
|
|
||||||
|
this.hideActions = function() {
|
||||||
|
this.actionsVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showActions = function() {
|
||||||
|
this.actionsVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isEmpty = function() {
|
||||||
|
return !self.rate() && !self.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function ItemModel(data) {
|
function ItemModel(data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.product_key = ko.observable('');
|
this.product_key = ko.observable('');
|
||||||
this.notes = ko.observable('');
|
this.notes = ko.observable('');
|
||||||
this.cost = ko.observable();
|
this.cost = ko.observable();
|
||||||
this.qty = ko.observable();
|
this.qty = ko.observable();
|
||||||
this.tax = ko.observable();
|
this.tax_rate = ko.observable();
|
||||||
|
this.tax_name = ko.observable('');
|
||||||
this.actionsVisible = ko.observable(false);
|
this.actionsVisible = ko.observable(false);
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -659,7 +763,7 @@
|
|||||||
write: function(value) {
|
write: function(value) {
|
||||||
value = wordWrapText(value);
|
value = wordWrapText(value);
|
||||||
self.notes(value);
|
self.notes(value);
|
||||||
onChange();
|
onItemChange();
|
||||||
},
|
},
|
||||||
owner: this
|
owner: this
|
||||||
});
|
});
|
||||||
@ -667,9 +771,9 @@
|
|||||||
this.rawTotal = ko.computed(function() {
|
this.rawTotal = ko.computed(function() {
|
||||||
var cost = parseFloat(self.cost());
|
var cost = parseFloat(self.cost());
|
||||||
var qty = parseFloat(self.qty());
|
var qty = parseFloat(self.qty());
|
||||||
var tax = parseFloat(self.tax());
|
var tax = parseFloat(self.tax_rate());
|
||||||
var value = cost * qty;
|
var value = cost * qty;
|
||||||
if (self.tax() > 0) {
|
if (tax > 0) {
|
||||||
//value = value * ((100 - this.tax())/100);
|
//value = value * ((100 - this.tax())/100);
|
||||||
}
|
}
|
||||||
return value ? value : '';
|
return value ? value : '';
|
||||||
@ -693,7 +797,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange()
|
function onItemChange()
|
||||||
{
|
{
|
||||||
var hasEmpty = false;
|
var hasEmpty = false;
|
||||||
for(var i=0; i<model.invoice_items().length; i++) {
|
for(var i=0; i<model.invoice_items().length; i++) {
|
||||||
@ -702,6 +806,7 @@
|
|||||||
hasEmpty = true;
|
hasEmpty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasEmpty) {
|
if (!hasEmpty) {
|
||||||
model.addItem();
|
model.addItem();
|
||||||
}
|
}
|
||||||
@ -711,6 +816,21 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onTaxRateChange()
|
||||||
|
{
|
||||||
|
var hasEmpty = false;
|
||||||
|
for(var i=0; i<model.tax_rates().length; i++) {
|
||||||
|
var taxRate = model.tax_rates()[i];
|
||||||
|
if (taxRate.isEmpty()) {
|
||||||
|
hasEmpty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasEmpty) {
|
||||||
|
model.addTaxRate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var products = {{ $products }};
|
var products = {{ $products }};
|
||||||
var clients = {{ $clients }};
|
var clients = {{ $clients }};
|
||||||
var clientMap = {};
|
var clientMap = {};
|
||||||
@ -740,7 +860,8 @@
|
|||||||
model.invoice_number('{{ $invoiceNumber }}');
|
model.invoice_number('{{ $invoiceNumber }}');
|
||||||
model.terms(wordWrapText('{{ $account->invoice_terms }}', 250));
|
model.terms(wordWrapText('{{ $account->invoice_terms }}', 250));
|
||||||
@endif
|
@endif
|
||||||
model.invoice_items.push(new ItemModel());
|
model.addItem();
|
||||||
|
model.addTaxRate();
|
||||||
ko.applyBindings(model);
|
ko.applyBindings(model);
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
||||||
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.js" type="text/javascript"></script>
|
|
||||||
|
<!-- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.js" type="text/javascript"></script> -->
|
||||||
|
<script src="{{ asset('js/jquery.js') }}" type="text/javascript"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ asset('css/bootstrap.css') }}"/>
|
<link rel="stylesheet" type="text/css" href="{{ asset('css/bootstrap.css') }}"/>
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +23,11 @@ div.panel {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sides-padded {
|
||||||
|
margin-left: 8px !important;
|
||||||
|
margin-right: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.form-horizontal {
|
.form-horizontal {
|
||||||
max-width: 750px;
|
max-width: 750px;
|
||||||
|
@ -15,8 +15,28 @@ function generatePDF(invoice) {
|
|||||||
var descriptionLeft = 140;
|
var descriptionLeft = 140;
|
||||||
var unitCostRight = 400;
|
var unitCostRight = 400;
|
||||||
var qtyRight = 470;
|
var qtyRight = 470;
|
||||||
|
var taxRight = 470;
|
||||||
var lineTotalRight = 540;
|
var lineTotalRight = 540;
|
||||||
|
|
||||||
|
|
||||||
|
var hasTaxes = true;
|
||||||
|
for (var i=0; i<invoice.invoice_items.length; i++)
|
||||||
|
{
|
||||||
|
var item = invoice.invoice_items[i];
|
||||||
|
if (item.tax_rate > 0) {
|
||||||
|
hasTaxes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTaxes)
|
||||||
|
{
|
||||||
|
descriptionLeft -= 20;
|
||||||
|
unitCostRight -= 40;
|
||||||
|
qtyRight -= 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var doc = new jsPDF('p', 'pt');
|
var doc = new jsPDF('p', 'pt');
|
||||||
doc.setFont('Helvetica','');
|
doc.setFont('Helvetica','');
|
||||||
doc.setFontSize(10);
|
doc.setFontSize(10);
|
||||||
@ -86,6 +106,7 @@ function generatePDF(invoice) {
|
|||||||
|
|
||||||
var costX = unitCostRight - (doc.getStringUnitWidth('Unit Cost') * doc.internal.getFontSize());
|
var costX = unitCostRight - (doc.getStringUnitWidth('Unit Cost') * doc.internal.getFontSize());
|
||||||
var qtyX = qtyRight - (doc.getStringUnitWidth('Quantity') * doc.internal.getFontSize());
|
var qtyX = qtyRight - (doc.getStringUnitWidth('Quantity') * doc.internal.getFontSize());
|
||||||
|
var taxX = taxRight - (doc.getStringUnitWidth('Tax') * doc.internal.getFontSize());
|
||||||
var totalX = lineTotalRight - (doc.getStringUnitWidth('Line Total') * doc.internal.getFontSize());
|
var totalX = lineTotalRight - (doc.getStringUnitWidth('Line Total') * doc.internal.getFontSize());
|
||||||
|
|
||||||
doc.text(tableLeft, tableTop, 'Item');
|
doc.text(tableLeft, tableTop, 'Item');
|
||||||
@ -94,13 +115,18 @@ function generatePDF(invoice) {
|
|||||||
doc.text(qtyX, tableTop, 'Quantity');
|
doc.text(qtyX, tableTop, 'Quantity');
|
||||||
doc.text(totalX, tableTop, 'Line Total');
|
doc.text(totalX, tableTop, 'Line Total');
|
||||||
|
|
||||||
|
if (hasTaxes)
|
||||||
|
{
|
||||||
|
doc.text(taxX, tableTop, 'Tax');
|
||||||
|
}
|
||||||
|
|
||||||
/* line items */
|
/* line items */
|
||||||
doc.setFontType("normal");
|
doc.setFontType("normal");
|
||||||
var line = 1;
|
var line = 1;
|
||||||
var total = 0;
|
var total = 0;
|
||||||
var shownItem = false;
|
var shownItem = false;
|
||||||
|
|
||||||
for(var i=0; i<invoice.invoice_items.length; i++) {
|
for (var i=0; i<invoice.invoice_items.length; i++) {
|
||||||
var item = invoice.invoice_items[i];
|
var item = invoice.invoice_items[i];
|
||||||
var cost = formatNumber(item.cost);
|
var cost = formatNumber(item.cost);
|
||||||
var qty = item.qty ? parseFloat(item.qty) + '' : '';
|
var qty = item.qty ? parseFloat(item.qty) + '' : '';
|
||||||
|
Loading…
Reference in New Issue
Block a user