working in taxes

This commit is contained in:
Hillel Coren 2013-12-29 01:33:48 +02:00
parent 130a176888
commit f323d0ed95
9 changed files with 151 additions and 62 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
/app/config/staging
/app/config/development
/app/storage/
/app/storage
/public/logo
/public/build
/bootstrap/compiled.php

View File

@ -348,6 +348,7 @@ class InvoiceController extends \BaseController {
'products' => Product::scope()->get(array('product_key','notes','cost','qty')),
'countries' => Country::orderBy('name')->get(),
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
'frequencies' => array(
1 => 'Weekly',
2 => 'Two weeks',

View File

@ -10,7 +10,6 @@ class ConfideSetupUsersTable extends Migration {
*/
public function up()
{
Schema::dropIfExists('tax_rates');
Schema::dropIfExists('themes');
Schema::dropIfExists('credits');
Schema::dropIfExists('activities');
@ -20,6 +19,7 @@ class ConfideSetupUsersTable extends Migration {
Schema::dropIfExists('payments');
Schema::dropIfExists('invoice_items');
Schema::dropIfExists('products');
Schema::dropIfExists('tax_rates');
Schema::dropIfExists('contacts');
Schema::dropIfExists('invoices');
Schema::dropIfExists('password_reminders');
@ -313,6 +313,24 @@ class ConfideSetupUsersTable extends Migration {
$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('products', function($t)
{
$t->increments('id');
@ -341,6 +359,7 @@ class ConfideSetupUsersTable extends Migration {
$t->unsignedInteger('user_id');
$t->unsignedInteger('invoice_id')->index();
$t->unsignedInteger('product_id')->nullable();
$t->unsignedInteger('tax_rate_id')->nullable();
$t->timestamps();
$t->softDeletes();
@ -354,6 +373,7 @@ class ConfideSetupUsersTable extends Migration {
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
$t->foreign('product_id')->references('id')->on('products');
$t->foreign('tax_rate_id')->references('id')->on('tax_rates');
$t->foreign('user_id')->references('id')->on('users');
$t->unsignedInteger('public_id');
@ -412,24 +432,6 @@ class ConfideSetupUsersTable extends Migration {
$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)
{
$t->increments('id');
@ -461,7 +463,6 @@ class ConfideSetupUsersTable extends Migration {
*/
public function down()
{
Schema::dropIfExists('tax_rates');
Schema::dropIfExists('themes');
Schema::dropIfExists('credits');
Schema::dropIfExists('activities');
@ -471,6 +472,7 @@ class ConfideSetupUsersTable extends Migration {
Schema::dropIfExists('payments');
Schema::dropIfExists('invoice_items');
Schema::dropIfExists('products');
Schema::dropIfExists('tax_rates');
Schema::dropIfExists('contacts');
Schema::dropIfExists('invoices');
Schema::dropIfExists('password_reminders');

View File

@ -47,9 +47,11 @@ class EntityModel extends Eloquent
public function scopeScope($query, $publicId = false, $accountId = false)
{
if (!$accountId) {
if (!$accountId)
{
$accountId = Auth::user()->account_id;
}
$query->whereAccountId($accountId);
if ($publicId)

View File

@ -4,6 +4,7 @@ use Invoice;
use InvoiceItem;
use Product;
use Utils;
use TaxRate;
class InvoiceRepository
{
@ -134,6 +135,24 @@ class InvoiceRepository
$product->save();
}
$taxRate = false;
if ($item->tax)
{
if ($item->tax->public_id)
{
$taxRate = TaxRate::scope($item->tax->public_id)->firstOrFail();
}
else
{
$taxRate = TaxRate::createNew();
}
$taxRate->rate = floatval($item->tax->rate);
$taxRate->name = trim($item->tax->name);
$taxRate->save();
}
$invoiceItem = InvoiceItem::createNew();
$invoiceItem->product_id = isset($product) ? $product->id : null;
$invoiceItem->product_key = trim($item->product_key);
@ -141,6 +160,13 @@ class InvoiceRepository
$invoiceItem->cost = floatval($item->cost);
$invoiceItem->qty = floatval($item->qty);
if ($taxRate)
{
$invoiceItem->tax_rate_id = $taxRate->id;
$invoiceItem->tax_rate = $taxRate->rate;
$invoiceItem->tax_name = $taxRate->name;
}
$invoice->invoice_items()->save($invoiceItem);
$total += floatval($item->qty) * floatval($item->cost);
}

View File

@ -5,7 +5,7 @@
@section('head')
<meta name="csrf-token" content="<?= csrf_token() ?>">
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<script src="{{ asset('js/jquery-ui.min.js') }}" type="text/javascript"></script>
@if (Auth::check() && Auth::user()->theme_id)
<link rel="stylesheet" type="text/css" href="{{ asset('css/themes/'.Auth::user()->theme->name.'.min.css') }}"/>
@else

View File

@ -114,7 +114,7 @@
<input onkeyup="onItemChange()" data-bind="value: qty, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
</td>
<td style="width:80px; vertical-align:middle" data-bind="visible: $parent.tax_rates().length > 1">
<select style="width:100%" data-bind="options: $parent.tax_rates"></select>
<select style="width:100%" data-bind="value: tax, options: $parent.tax_rates, optionsText: 'displayName'"></select>
</td>
<td style="width:100px;text-align: right;padding-top:9px !important">
<span data-bind="text: total"></span>
@ -126,7 +126,8 @@
</tbody>
<tfoot>
<tr>
<td class="hide-border" data-bind="attr: {colspan: tax_rates().length > 1 ? 4 : 3}"/>
<td class="hide-border"/>
<td data-bind="attr: {colspan: tax_rates().length > 1 ? 3 : 2}"/>
<td colspan="2">Subtotal</td>
<td style="text-align: right"><span data-bind="text: subtotal"/></td>
</tr>
@ -352,22 +353,21 @@
//$('[name="client_combobox"]').focus();
@endif
$('#clientModal').on('hidden.bs.modal', function () {
$('#clientModal').on('shown.bs.modal', function () {
$('#name').focus();
}).on('hidden.bs.modal', function () {
if (model.clientBackup) {
console.log("Loading backup");
//console.log(model.clientBackup);
model.loadClient(model.clientBackup);
refreshPDF();
}
})
$('#clientModal').on('shown.bs.modal', function () {
$('#name').focus();
})
$('#taxModal').on('shown.bs.modal', function () {
$('#taxModal input:first').focus();
}).on('hidden.bs.modal', function () {
if (model.taxBackup) {
}
})
$('#actionDropDown > button:first').click(function() {
@ -557,10 +557,13 @@
self.showTaxesForm = function() {
self.taxBackup = ko.mapping.toJS(self.tax_rates);
$('#taxModal').modal('show');
}
self.taxFormComplete = function() {
model.taxBackup = false;
$('#taxModal').modal('hide');
}
@ -606,8 +609,14 @@
}
self.addItem = function() {
self.invoice_items.push(new ItemModel());
var itemModel = new ItemModel();
self.invoice_items.push(itemModel);
applyComboboxListeners();
itemModel.tax.subscribe(function (data) {
console.log('Tax change...');
console.log(data)
});
}
self.removeTaxRate = function(taxRate) {
@ -615,8 +624,8 @@
//refreshPDF();
}
self.addTaxRate = function() {
self.tax_rates.push(new TaxRateModel());
self.addTaxRate = function(data) {
self.tax_rates.push(new TaxRateModel(data));
applyComboboxListeners();
}
@ -725,19 +734,30 @@
function TaxRateModel(data) {
var self = this;
this.rate = ko.observable();
this.name = ko.observable('');
this.actionsVisible = ko.observable(false);
self.public_id = ko.observable('');
self.rate = ko.observable();
self.name = ko.observable('');
self.actionsVisible = ko.observable(false);
this.hideActions = function() {
this.actionsVisible(false);
if (data) {
ko.mapping.fromJS(data, {}, this);
}
this.showActions = function() {
this.actionsVisible(true);
self.displayName = ko.computed(function() {
var name = self.name() ? self.name() : false;
var rate = self.rate() ? self.rate() : false;
return (name && rate) ? (rate + '%' + ' - ' + name) : '';
});
self.hideActions = function() {
self.actionsVisible(false);
}
this.isEmpty = function() {
self.showActions = function() {
self.actionsVisible(true);
}
self.isEmpty = function() {
return !self.rate() && !self.name();
}
}
@ -748,14 +768,23 @@
this.notes = ko.observable('');
this.cost = ko.observable();
this.qty = ko.observable();
this.tax_rate = ko.observable();
this.tax_name = ko.observable('');
this.tax = ko.observable();
this.actionsVisible = ko.observable(false);
if (data) {
ko.mapping.fromJS(data, {}, this);
}
console.log('data: ' + data);
for (var i=0; i<model.tax_rates().length; i++) {
var taxRate = model.tax_rates()[i];
if (data && (data.tax_name == taxRate.name())) {
self.tax(taxRate);
} else if (!data && !taxRate.tax_name) {
self.tax(taxRate);
}
}
self.wrapped_notes = ko.computed({
read: function() {
return this.notes();
@ -771,10 +800,10 @@
this.rawTotal = ko.computed(function() {
var cost = parseFloat(self.cost());
var qty = parseFloat(self.qty());
var tax = parseFloat(self.tax_rate());
var taxRate = self.tax() ? parseFloat(self.tax().rate()) : 0;
var value = cost * qty;
if (tax > 0) {
//value = value * ((100 - this.tax())/100);
if (taxRate > 0) {
value += value * (taxRate/100);
}
return value ? value : '';
});
@ -793,7 +822,11 @@
}
this.isEmpty = function() {
return !self.product_key() && !self.notes() && !self.cost() && !self.qty() && !self.tax();
return !self.product_key() && !self.notes() && !self.cost() && !self.qty();
}
this.onSelect = function(){
console.log("select");
}
}
@ -846,6 +879,9 @@
window.model = new InvoiceModel();
@foreach ($taxRates as $taxRate)
model.addTaxRate({{ $taxRate }});
@endforeach
@if ($invoice)
var invoice = {{ $invoice }};
ko.mapping.fromJS(invoice, model.mapping, model);
@ -860,11 +896,10 @@
model.invoice_number('{{ $invoiceNumber }}');
model.terms(wordWrapText('{{ $account->invoice_terms }}', 250));
@endif
model.addItem();
model.addTaxRate();
model.addItem();
ko.applyBindings(model);
</script>
@stop

12
public/js/jquery-ui.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -132,6 +132,7 @@ function generatePDF(invoice) {
var qty = item.qty ? parseFloat(item.qty) + '' : '';
var notes = item.notes;
var productKey = item.product_key;
var tax = item.tax && parseFloat(item.tax.rate) ? parseFloat(item.tax.rate) + '%' : false;
// show at most one blank line
if (shownItem && (!cost || cost == '0.00') && !qty && !notes && !productKey) {
@ -144,11 +145,17 @@ function generatePDF(invoice) {
productKey = processVariables(productKey);
var lineTotal = item.cost * item.qty;
if (lineTotal) total += lineTotal;
if (tax) {
lineTotal += lineTotal * parseFloat(item.tax.rate) / 100;
}
if (lineTotal) {
total += lineTotal;
}
lineTotal = formatNumber(lineTotal);
var costX = unitCostRight - (doc.getStringUnitWidth(cost) * doc.internal.getFontSize());
var qtyX = qtyRight - (doc.getStringUnitWidth(qty) * doc.internal.getFontSize());
var taxX = taxRight - (doc.getStringUnitWidth(tax) * doc.internal.getFontSize());
var totalX = lineTotalRight - (doc.getStringUnitWidth(lineTotal) * doc.internal.getFontSize());
var x = tableTop + (line * rowHeight) + 6;
@ -158,6 +165,10 @@ function generatePDF(invoice) {
doc.text(qtyX, x, qty);
doc.text(totalX, x, lineTotal);
if (tax) {
doc.text(taxX, x, tax);
}
line += doc.splitTextToSize(item.notes, 200).length;
}