1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-09 12:42:36 +01:00

Improved inclusive tax rates

This commit is contained in:
Hillel Coren 2017-12-03 13:56:10 +02:00
parent 8d110c2a4b
commit a99f45c628
13 changed files with 153 additions and 67 deletions

View File

@ -177,6 +177,7 @@ class Account extends Eloquent
'credit_number_prefix',
'credit_number_pattern',
'task_rate',
'inclusive_taxes',
];
/**

View File

@ -987,6 +987,7 @@ class Invoice extends EntityModel implements BalanceAffecting
'include_item_taxes_inline',
'invoice_fields',
'show_currency_code',
'inclusive_taxes',
]);
foreach ($this->invoice_items as $invoiceItem) {
@ -1344,17 +1345,26 @@ class Invoice extends EntityModel implements BalanceAffecting
public function getTaxes($calculatePaid = false)
{
$taxes = [];
$account = $this->account;
$taxable = $this->getTaxable();
$paidAmount = $this->getAmountPaid($calculatePaid);
if ($this->tax_name1) {
$invoiceTaxAmount = round($taxable * ($this->tax_rate1 / 100), 2);
if ($account->inclusive_taxes) {
$invoiceTaxAmount = round(($taxable * 100) / (100 + ($this->tax_rate1 * 100)), 2);
} else {
$invoiceTaxAmount = round($taxable * ($this->tax_rate1 / 100), 2);
}
$invoicePaidAmount = floatval($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
$this->calculateTax($taxes, $this->tax_name1, $this->tax_rate1, $invoiceTaxAmount, $invoicePaidAmount);
}
if ($this->tax_name2) {
$invoiceTaxAmount = round($taxable * ($this->tax_rate2 / 100), 2);
if ($account->inclusive_taxes) {
$invoiceTaxAmount = round(($taxable * 100) / (100 + ($this->tax_rate2 * 100)), 2);
} else {
$invoiceTaxAmount = round($taxable * ($this->tax_rate2 / 100), 2);
}
$invoicePaidAmount = floatval($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
$this->calculateTax($taxes, $this->tax_name2, $this->tax_rate2, $invoiceTaxAmount, $invoicePaidAmount);
}
@ -1363,13 +1373,21 @@ class Invoice extends EntityModel implements BalanceAffecting
$itemTaxable = $this->getItemTaxable($invoiceItem, $taxable);
if ($invoiceItem->tax_name1) {
$itemTaxAmount = round($itemTaxable * ($invoiceItem->tax_rate1 / 100), 2);
if ($account->inclusive_taxes) {
$itemTaxAmount = round(($itemTaxable * 100) / (100 + ($invoiceItem->tax_rate1 * 100)), 2);
} else {
$itemTaxAmount = round($itemTaxable * ($invoiceItem->tax_rate1 / 100), 2);
}
$itemPaidAmount = floatval($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
$this->calculateTax($taxes, $invoiceItem->tax_name1, $invoiceItem->tax_rate1, $itemTaxAmount, $itemPaidAmount);
}
if ($invoiceItem->tax_name2) {
$itemTaxAmount = round($itemTaxable * ($invoiceItem->tax_rate2 / 100), 2);
if ($account->inclusive_taxes) {
$itemTaxAmount = round(($itemTaxable * 100) / (100 + ($invoiceItem->tax_rate2 * 100)), 2);
} else {
$itemTaxAmount = round($itemTaxable * ($invoiceItem->tax_rate2 / 100), 2);
}
$itemPaidAmount = floatval($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
$this->calculateTax($taxes, $invoiceItem->tax_name2, $invoiceItem->tax_rate2, $itemTaxAmount, $itemPaidAmount);
}

View File

@ -26,7 +26,11 @@ class TaxRateDatatable extends EntityDatatable
[
'type',
function ($model) {
return $model->is_inclusive ? trans('texts.inclusive') : trans('texts.exclusive');
if (auth()->user()->account->inclusive_taxes) {
return trans('texts.standard');
} else {
return $model->is_inclusive ? trans('texts.inclusive') : trans('texts.exclusive');
}
},
],
];

View File

@ -607,10 +607,12 @@ class InvoiceRepository extends BaseRepository
$total += $invoice->custom_value2;
}
$taxAmount1 = round($total * ($invoice->tax_rate1 ? $invoice->tax_rate1 : 0) / 100, 2);
$taxAmount2 = round($total * ($invoice->tax_rate2 ? $invoice->tax_rate2 : 0) / 100, 2);
$total = round($total + $taxAmount1 + $taxAmount2, 2);
$total += $itemTax;
if (! $account->inclusive_taxes) {
$taxAmount1 = round($total * ($invoice->tax_rate1 ? $invoice->tax_rate1 : 0) / 100, 2);
$taxAmount2 = round($total * ($invoice->tax_rate2 ? $invoice->tax_rate2 : 0) / 100, 2);
$total = round($total + $taxAmount1 + $taxAmount2, 2);
$total += $itemTax;
}
// custom fields not charged taxes
if ($invoice->custom_value1 && ! $invoice->custom_taxes1) {

View File

@ -275,6 +275,7 @@ class AccountTransformer extends EntityTransformer
'custom_contact_label1' => $account->custom_contact_label1,
'custom_contact_label2' => $account->custom_contact_label2,
'task_rate' => (float) $account->task_rate,
'inclusive_taxes' => (bool) $account->inclusive_taxes,
];
}
}

View File

@ -92,6 +92,9 @@ class AddSubdomainToLookups extends Migration
$table->unique(['account_id', 'public_id']);
});
Schema::table('accounts', function ($table) {
$table->boolean('inclusive_taxes')->default(0);
});
}
/**
@ -137,5 +140,9 @@ class AddSubdomainToLookups extends Migration
$table->dropColumn('user_id');
});
Schema::table('accounts', function ($table) {
$table->dropColumn('inclusive_taxes');
});
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -631,7 +631,11 @@ function calculateAmounts(invoice) {
}
}
var taxAmount1 = roundToTwo(lineTotal * taxRate1 / 100);
if (invoice.account.inclusive_taxes != '1') {
var taxAmount1 = roundToTwo(lineTotal * taxRate1 / 100);
} else {
var taxAmount1 = roundToTwo((lineTotal * 100) / (100 + (taxRate1 * 100)));
}
if (taxAmount1 != 0 || taxName1) {
hasTaxes = true;
var key = taxName1 + taxRate1;
@ -642,7 +646,11 @@ function calculateAmounts(invoice) {
}
}
var taxAmount2 = roundToTwo(lineTotal * taxRate2 / 100);
if (invoice.account.inclusive_taxes != '1') {
var taxAmount2 = roundToTwo(lineTotal * taxRate2 / 100);
} else {
var taxAmount2 = roundToTwo((lineTotal * 100) / (100 + (taxRate2 * 100)));
}
if (taxAmount2 != 0 || taxName2) {
hasTaxes = true;
var key = taxName2 + taxRate2;
@ -683,14 +691,20 @@ function calculateAmounts(invoice) {
if (parseFloat(invoice.tax_rate2 || 0) != 0) {
taxRate2 = parseFloat(invoice.tax_rate2);
}
taxAmount1 = roundToTwo(total * taxRate1 / 100);
taxAmount2 = roundToTwo(total * taxRate2 / 100);
total = total + taxAmount1 + taxAmount2;
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
total += taxes[key].amount;
}
if (invoice.account.inclusive_taxes != '1') {
taxAmount1 = roundToTwo(total * taxRate1 / 100);
taxAmount2 = roundToTwo(total * taxRate2 / 100);
total = total + taxAmount1 + taxAmount2;
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
total += taxes[key].amount;
}
}
} else {
taxAmount1 = roundToTwo((total * 100) / (100 + (taxRate1 * 100)));
taxAmount2 = roundToTwo((total * 100) / (100 + (taxRate2 * 100)));
}
// custom fields w/o with taxes

View File

@ -2604,6 +2604,9 @@ $LANG = array(
'processing_request' => 'Processing request',
'mcrypt_warning' => 'Warning: Mcrypt is deprecated, run <code>php artisan ninja:update-key --legacy=true</code> to update your cipher.',
'edit_times' => 'Edit Times',
'inclusive_taxes_help' => 'Include <b>taxes in the cost</b>',
'inclusive_taxes_warning' => 'Warning: existing invoices will need to be resaved',
'standard' => 'Standard',
);

View File

@ -28,13 +28,14 @@
{!! Former::text('name')->label('texts.name') !!}
{!! Former::text('rate')->label('texts.rate')->append('%') !!}
{!! Former::radios('is_inclusive')->radios([
trans('texts.exclusive') . ': 100 + 10% = 100 + 10' => array('name' => 'is_inclusive', 'value' => 0),
trans('texts.inclusive') . ':&nbsp; 100 + 10% = 90.91 + 9.09' => array('name' => 'is_inclusive', 'value' => 1),
])->check(0)
->label('type')
->help('tax_rate_type_help') !!}
@if (! auth()->user()->account->inclusive_taxes)
{!! Former::radios('is_inclusive')->radios([
trans('texts.exclusive') . ': 100 + 10% = 100 + 10' => array('name' => 'is_inclusive', 'value' => 0),
trans('texts.inclusive') . ':&nbsp; 100 + 10% = 90.91 + 9.09' => array('name' => 'is_inclusive', 'value' => 1),
])->check(0)
->label('type')
->help('tax_rate_type_help') !!}
@endif
</div>
</div>

View File

@ -11,6 +11,7 @@
{{ Former::populateField('invoice_item_taxes', intval($account->invoice_item_taxes)) }}
{{ Former::populateField('enable_second_tax_rate', intval($account->enable_second_tax_rate)) }}
{{ Former::populateField('include_item_taxes_inline', intval($account->include_item_taxes_inline)) }}
{{ Former::populateField('inclusive_taxes', intval($account->inclusive_taxes)) }}
<div class="panel panel-default">
@ -29,13 +30,19 @@
->label('&nbsp;')
->value(1) !!}
{!! Former::checkbox('include_item_taxes_inline')
->text(trans('texts.include_item_taxes_inline'))
{!! Former::checkbox('enable_second_tax_rate')
->text(trans('texts.enable_second_tax_rate'))
->label('&nbsp;')
->value(1) !!}
{!! Former::checkbox('enable_second_tax_rate')
->text(trans('texts.enable_second_tax_rate'))
{!! Former::checkbox('inclusive_taxes')
->text(trans('texts.inclusive_taxes_help'))
->label('&nbsp;')
->value(1) !!}
{!! Former::checkbox('include_item_taxes_inline')
->text(trans('texts.include_item_taxes_inline'))
->label('&nbsp;')
->value(1) !!}
@ -75,4 +82,14 @@
</script>
<script type="text/javascript">
$(function() {
@if (App\Models\Invoice::scope()->withTrashed()->count())
$('#inclusive_taxes').change(function() {
swal("{{ trans('texts.inclusive_taxes_warning') }}");
})
@endif
})
</script>
@stop

View File

@ -425,10 +425,18 @@ function InvoiceModel(data) {
}
var taxRate1 = parseFloat(self.tax_rate1());
var tax1 = roundToTwo(total * (taxRate1/100));
@if ($account->inclusive_taxes)
var tax1 = roundToTwo((total * 100) / (100 + (taxRate1 * 100)));
@else
var tax1 = roundToTwo(total * (taxRate1/100));
@endif
var taxRate2 = parseFloat(self.tax_rate2());
var tax2 = roundToTwo(total * (taxRate2/100));
@if ($account->inclusive_taxes)
var tax2 = roundToTwo((total * 100) / (100 + (taxRate2 * 100)));
@else
var tax2 = roundToTwo(total * (taxRate2/100));
@endif
return self.formatMoney(tax1 + tax2);
});
@ -447,7 +455,11 @@ function InvoiceModel(data) {
}
}
var taxAmount = roundToTwo(lineTotal * item.tax_rate1() / 100);
@if ($account->inclusive_taxes)
var taxAmount = roundToTwo((lineTotal * 100) / (100 + (item.tax_rate1() * 100)));
@else
var taxAmount = roundToTwo(lineTotal * item.tax_rate1() / 100);
@endif
if (taxAmount) {
var key = item.tax_name1() + item.tax_rate1();
if (taxes.hasOwnProperty(key)) {
@ -457,7 +469,11 @@ function InvoiceModel(data) {
}
}
var taxAmount = roundToTwo(lineTotal * item.tax_rate2() / 100);
@if ($account->inclusive_taxes)
var taxAmount = roundToTwo((lineTotal * 100) / (100 + (item.tax_rate2() * 100)));
@else
var taxAmount = roundToTwo(lineTotal * item.tax_rate2() / 100);
@endif
if (taxAmount) {
var key = item.tax_name2() + item.tax_rate2();
if (taxes.hasOwnProperty(key)) {
@ -529,19 +545,21 @@ function InvoiceModel(data) {
total = NINJA.parseFloat(total) + customValue2;
}
var taxAmount1 = roundToTwo(total * parseFloat(self.tax_rate1()) / 100);
var taxAmount2 = roundToTwo(total * parseFloat(self.tax_rate2()) / 100);
@if (! $account->inclusive_taxes)
var taxAmount1 = roundToTwo(total * parseFloat(self.tax_rate1()) / 100);
var taxAmount2 = roundToTwo(total * parseFloat(self.tax_rate2()) / 100);
total = NINJA.parseFloat(total) + taxAmount1 + taxAmount2;
total = roundToTwo(total);
total = NINJA.parseFloat(total) + taxAmount1 + taxAmount2;
total = roundToTwo(total);
var taxes = self.totals.itemTaxes();
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
total += taxes[key].amount;
total = roundToTwo(total);
var taxes = self.totals.itemTaxes();
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
total += taxes[key].amount;
total = roundToTwo(total);
}
}
}
@endif
if (customValue1 && !customTaxes1) {
total = NINJA.parseFloat(total) + customValue1;