mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-07 16:04:32 -04:00
bug fixes
This commit is contained in:
parent
f323d0ed95
commit
4ff55936d7
@ -3,20 +3,23 @@
|
|||||||
use ninja\mailers\ContactMailer as Mailer;
|
use ninja\mailers\ContactMailer as Mailer;
|
||||||
use ninja\repositories\InvoiceRepository;
|
use ninja\repositories\InvoiceRepository;
|
||||||
use ninja\repositories\ClientRepository;
|
use ninja\repositories\ClientRepository;
|
||||||
|
use ninja\repositories\TaxRateRepository;
|
||||||
|
|
||||||
class InvoiceController extends \BaseController {
|
class InvoiceController extends \BaseController {
|
||||||
|
|
||||||
protected $mailer;
|
protected $mailer;
|
||||||
protected $invoiceRepo;
|
protected $invoiceRepo;
|
||||||
protected $clientRepo;
|
protected $clientRepo;
|
||||||
|
protected $taxRateRepo;
|
||||||
|
|
||||||
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo)
|
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, TaxRateRepository $taxRateRepo)
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->mailer = $mailer;
|
$this->mailer = $mailer;
|
||||||
$this->invoiceRepo = $invoiceRepo;
|
$this->invoiceRepo = $invoiceRepo;
|
||||||
$this->clientRepo = $clientRepo;
|
$this->clientRepo = $clientRepo;
|
||||||
|
$this->taxRateRepo = $taxRateRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
@ -391,13 +394,15 @@ class InvoiceController extends \BaseController {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
$this->taxRateRepo->save($input->tax_rates);
|
||||||
|
|
||||||
$clientData = (array) $input->client;
|
$clientData = (array) $input->client;
|
||||||
$client = $this->clientRepo->save($input->client->public_id, $clientData);
|
$client = $this->clientRepo->save($input->client->public_id, $clientData);
|
||||||
|
|
||||||
$invoiceData = (array) $input;
|
$invoiceData = (array) $input;
|
||||||
$invoiceData['client_id'] = $client->id;
|
$invoiceData['client_id'] = $client->id;
|
||||||
$invoice = $this->invoiceRepo->save($publicId, $invoiceData);
|
$invoice = $this->invoiceRepo->save($publicId, $invoiceData);
|
||||||
|
|
||||||
if ($action == 'email' && $invoice->invoice_status_id == INVOICE_STATUS_DRAFT)
|
if ($action == 'email' && $invoice->invoice_status_id == INVOICE_STATUS_DRAFT)
|
||||||
{
|
{
|
||||||
$invoice->invoice_status_id = INVOICE_STATUS_SENT;
|
$invoice->invoice_status_id = INVOICE_STATUS_SENT;
|
||||||
|
@ -359,7 +359,6 @@ class ConfideSetupUsersTable extends Migration {
|
|||||||
$t->unsignedInteger('user_id');
|
$t->unsignedInteger('user_id');
|
||||||
$t->unsignedInteger('invoice_id')->index();
|
$t->unsignedInteger('invoice_id')->index();
|
||||||
$t->unsignedInteger('product_id')->nullable();
|
$t->unsignedInteger('product_id')->nullable();
|
||||||
$t->unsignedInteger('tax_rate_id')->nullable();
|
|
||||||
$t->timestamps();
|
$t->timestamps();
|
||||||
$t->softDeletes();
|
$t->softDeletes();
|
||||||
|
|
||||||
@ -373,7 +372,6 @@ class ConfideSetupUsersTable extends Migration {
|
|||||||
|
|
||||||
$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('tax_rate_id')->references('id')->on('tax_rates');
|
|
||||||
$t->foreign('user_id')->references('id')->on('users');
|
$t->foreign('user_id')->references('id')->on('users');
|
||||||
|
|
||||||
$t->unsignedInteger('public_id');
|
$t->unsignedInteger('public_id');
|
||||||
|
@ -25,6 +25,11 @@ class Account extends Eloquent
|
|||||||
return $this->hasMany('AccountGateway');
|
return $this->hasMany('AccountGateway');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function tax_rates()
|
||||||
|
{
|
||||||
|
return $this->hasMany('TaxRate');
|
||||||
|
}
|
||||||
|
|
||||||
public function country()
|
public function country()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('Country');
|
return $this->belongsTo('Country');
|
||||||
|
@ -2,5 +2,6 @@
|
|||||||
|
|
||||||
class TaxRate extends EntityModel
|
class TaxRate extends EntityModel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -135,24 +135,6 @@ class InvoiceRepository
|
|||||||
$product->save();
|
$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 = InvoiceItem::createNew();
|
||||||
$invoiceItem->product_id = isset($product) ? $product->id : null;
|
$invoiceItem->product_id = isset($product) ? $product->id : null;
|
||||||
$invoiceItem->product_key = trim($item->product_key);
|
$invoiceItem->product_key = trim($item->product_key);
|
||||||
@ -160,11 +142,10 @@ class InvoiceRepository
|
|||||||
$invoiceItem->cost = floatval($item->cost);
|
$invoiceItem->cost = floatval($item->cost);
|
||||||
$invoiceItem->qty = floatval($item->qty);
|
$invoiceItem->qty = floatval($item->qty);
|
||||||
|
|
||||||
if ($taxRate)
|
if ($item->tax && isset($item->tax->rate) && isset($item->tax->name))
|
||||||
{
|
{
|
||||||
$invoiceItem->tax_rate_id = $taxRate->id;
|
$invoiceItem->tax_rate = floatval($item->tax->rate);
|
||||||
$invoiceItem->tax_rate = $taxRate->rate;
|
$invoiceItem->tax_name = trim($item->tax->name);
|
||||||
$invoiceItem->tax_name = $taxRate->name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoice->invoice_items()->save($invoiceItem);
|
$invoice->invoice_items()->save($invoiceItem);
|
||||||
|
49
app/ninja/repositories/TaxRateRepository.php
Executable file
49
app/ninja/repositories/TaxRateRepository.php
Executable file
@ -0,0 +1,49 @@
|
|||||||
|
<?php namespace ninja\repositories;
|
||||||
|
|
||||||
|
use TaxRate;
|
||||||
|
|
||||||
|
class TaxRateRepository
|
||||||
|
{
|
||||||
|
public function save($taxRates)
|
||||||
|
{
|
||||||
|
$taxRateIds = [];
|
||||||
|
|
||||||
|
foreach ($taxRates as $record)
|
||||||
|
{
|
||||||
|
if (!isset($record->rate) || $record->is_deleted)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!floatval($record->rate) || !trim($record->name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($record->public_id)
|
||||||
|
{
|
||||||
|
$taxRate = TaxRate::scope($record->public_id)->firstOrFail();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$taxRate = TaxRate::createNew();
|
||||||
|
}
|
||||||
|
|
||||||
|
$taxRate->rate = floatval($record->rate);
|
||||||
|
$taxRate->name = trim($record->name);
|
||||||
|
$taxRate->save();
|
||||||
|
|
||||||
|
$taxRateIds[] = $taxRate->public_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$taxRates = TaxRate::scope()->get();
|
||||||
|
|
||||||
|
foreach($taxRates as $taxRate)
|
||||||
|
{
|
||||||
|
if (!in_array($taxRate->public_id, $taxRateIds))
|
||||||
|
{
|
||||||
|
$taxRate->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -111,10 +111,10 @@
|
|||||||
<input onkeyup="onItemChange()" 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="onItemChange()" data-bind="value: qty, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
<input onkeyup="onItemChange()" data-bind="value: prettyQty, 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:120px; vertical-align:middle" data-bind="visible: $parent.tax_rates().length > 1">
|
||||||
<select style="width:100%" data-bind="value: tax, options: $parent.tax_rates, optionsText: 'displayName'"></select>
|
<select class="form-control" style="width:100%" data-bind="value: tax, options: $parent.tax_rates, optionsText: 'displayName'"></select>
|
||||||
</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>
|
||||||
@ -286,7 +286,7 @@
|
|||||||
<input onkeyup="onTaxRateChange()" data-bind="value: name, valueUpdate: 'afterkeydown'" class="form-control" onchange="refreshPDF()"//>
|
<input onkeyup="onTaxRateChange()" data-bind="value: name, valueUpdate: 'afterkeydown'" class="form-control" onchange="refreshPDF()"//>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:60px">
|
<td style="width:60px">
|
||||||
<input onkeyup="onTaxRateChange()" data-bind="value: rate, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
<input onkeyup="onTaxRateChange()" data-bind="value: prettyRate, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:10px; cursor:pointer" class="hide-border td-icon">
|
<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"/>
|
<i data-bind="click: $parent.removeTaxRate, visible: actionsVisible() && $parent.tax_rates().length > 1" class="fa fa-minus-circle" title="Remove item"/>
|
||||||
@ -298,7 +298,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer" style="margin-top: 0px">
|
<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-default" data-dismiss="modal">Cancel</button> -->
|
||||||
<button type="button" class="btn btn-primary" data-bind="click: taxFormComplete">Done</button>
|
<button type="button" class="btn btn-primary" data-bind="click: taxFormComplete">Done</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -392,9 +392,9 @@
|
|||||||
var product = products[i];
|
var product = products[i];
|
||||||
if (product.product_key == key) {
|
if (product.product_key == key) {
|
||||||
var model = ko.dataFor(this);
|
var model = ko.dataFor(this);
|
||||||
model.notes(product.notes);
|
//model.notes(product.notes);
|
||||||
model.cost(product.cost);
|
//model.cost(product.cost);
|
||||||
model.qty(product.qty);
|
//model.qty(product.qty);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -611,12 +611,7 @@
|
|||||||
self.addItem = function() {
|
self.addItem = function() {
|
||||||
var itemModel = new ItemModel();
|
var itemModel = new ItemModel();
|
||||||
self.invoice_items.push(itemModel);
|
self.invoice_items.push(itemModel);
|
||||||
applyComboboxListeners();
|
applyComboboxListeners();
|
||||||
|
|
||||||
itemModel.tax.subscribe(function (data) {
|
|
||||||
console.log('Tax change...');
|
|
||||||
console.log(data)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.removeTaxRate = function(taxRate) {
|
self.removeTaxRate = function(taxRate) {
|
||||||
@ -625,7 +620,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.addTaxRate = function(data) {
|
self.addTaxRate = function(data) {
|
||||||
self.tax_rates.push(new TaxRateModel(data));
|
var itemModel = new TaxRateModel(data);
|
||||||
|
self.tax_rates.push(itemModel);
|
||||||
applyComboboxListeners();
|
applyComboboxListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,15 +733,27 @@
|
|||||||
self.public_id = ko.observable('');
|
self.public_id = ko.observable('');
|
||||||
self.rate = ko.observable();
|
self.rate = ko.observable();
|
||||||
self.name = ko.observable('');
|
self.name = ko.observable('');
|
||||||
|
self.is_deleted = ko.observable(false);
|
||||||
self.actionsVisible = ko.observable(false);
|
self.actionsVisible = ko.observable(false);
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
ko.mapping.fromJS(data, {}, this);
|
ko.mapping.fromJS(data, {}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.prettyRate = ko.computed({
|
||||||
|
read: function () {
|
||||||
|
return this.rate() ? parseFloat(this.rate()) : '';
|
||||||
|
},
|
||||||
|
write: function (value) {
|
||||||
|
this.rate(value);
|
||||||
|
},
|
||||||
|
owner: this
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
self.displayName = ko.computed(function() {
|
self.displayName = ko.computed(function() {
|
||||||
var name = self.name() ? self.name() : false;
|
var name = self.name() ? self.name() : false;
|
||||||
var rate = self.rate() ? self.rate() : false;
|
var rate = self.rate() ? parseFloat(self.rate()) : false;
|
||||||
return (name && rate) ? (rate + '%' + ' - ' + name) : '';
|
return (name && rate) ? (rate + '%' + ' - ' + name) : '';
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -771,19 +779,40 @@
|
|||||||
this.tax = ko.observable();
|
this.tax = ko.observable();
|
||||||
this.actionsVisible = ko.observable(false);
|
this.actionsVisible = ko.observable(false);
|
||||||
|
|
||||||
|
this.prettyQty = ko.computed({
|
||||||
|
read: function () {
|
||||||
|
return this.qty() ? parseFloat(this.qty()) : '';
|
||||||
|
},
|
||||||
|
write: function (value) {
|
||||||
|
this.qty(value);
|
||||||
|
},
|
||||||
|
owner: this
|
||||||
|
});
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
ko.mapping.fromJS(data, {}, this);
|
ko.mapping.fromJS(data, {}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('data: ' + data);
|
|
||||||
for (var i=0; i<model.tax_rates().length; i++) {
|
for (var i=0; i<model.tax_rates().length; i++) {
|
||||||
var taxRate = model.tax_rates()[i];
|
var taxRate = model.tax_rates()[i];
|
||||||
if (data && (data.tax_name == taxRate.name())) {
|
if (data && (data.tax_name == taxRate.name() && data.tax_rate == taxRate.rate())) {
|
||||||
self.tax(taxRate);
|
self.tax(taxRate);
|
||||||
} else if (!data && !taxRate.tax_name) {
|
break;
|
||||||
|
} else if ((!data || !data.tax_name) && !taxRate.name()) {
|
||||||
self.tax(taxRate);
|
self.tax(taxRate);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the tax was deleted but exists for the line item
|
||||||
|
if (data && data.tax_name && (parseFloat(data.tax_rate)) && !self.tax()) {
|
||||||
|
var taxRate = new TaxRateModel();
|
||||||
|
taxRate.rate(parseFloat(data.tax_rate));
|
||||||
|
taxRate.name(data.tax_name);
|
||||||
|
taxRate.is_deleted(true);
|
||||||
|
model.tax_rates.push(taxRate);
|
||||||
|
self.tax(taxRate);
|
||||||
|
}
|
||||||
|
|
||||||
self.wrapped_notes = ko.computed({
|
self.wrapped_notes = ko.computed({
|
||||||
read: function() {
|
read: function() {
|
||||||
@ -882,6 +911,7 @@
|
|||||||
@foreach ($taxRates as $taxRate)
|
@foreach ($taxRates as $taxRate)
|
||||||
model.addTaxRate({{ $taxRate }});
|
model.addTaxRate({{ $taxRate }});
|
||||||
@endforeach
|
@endforeach
|
||||||
|
model.addTaxRate();
|
||||||
@if ($invoice)
|
@if ($invoice)
|
||||||
var invoice = {{ $invoice }};
|
var invoice = {{ $invoice }};
|
||||||
ko.mapping.fromJS(invoice, model.mapping, model);
|
ko.mapping.fromJS(invoice, model.mapping, model);
|
||||||
@ -896,7 +926,6 @@
|
|||||||
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.addTaxRate();
|
|
||||||
model.addItem();
|
model.addItem();
|
||||||
ko.applyBindings(model);
|
ko.applyBindings(model);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user