mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Support higher precision for unit cost and quantity #252
This commit is contained in:
parent
4e285c8ac6
commit
7a4a5d51f3
@ -1228,6 +1228,18 @@ class Utils
|
|||||||
return round($amount + $tax1 + $tax2, 2);
|
return round($amount + $tax1 + $tax2, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function roundSignificant($value, $precision = 2) {
|
||||||
|
if (round($value, 3) != $value) {
|
||||||
|
$precision = 4;
|
||||||
|
} elseif (round($value, 2) != $value) {
|
||||||
|
$precision = 3;
|
||||||
|
} elseif (round($value, 1) != $value) {
|
||||||
|
$precision = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return number_format($value, $precision, '.', '');
|
||||||
|
}
|
||||||
|
|
||||||
public static function truncateString($string, $length)
|
public static function truncateString($string, $length)
|
||||||
{
|
{
|
||||||
return strlen($string) > $length ? rtrim(substr($string, 0, $length)) . '...' : $string;
|
return strlen($string) > $length ? rtrim(substr($string, 0, $length)) . '...' : $string;
|
||||||
|
@ -4,6 +4,7 @@ namespace App\Ninja\Reports;
|
|||||||
|
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use Auth;
|
use Auth;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
class ProductReport extends AbstractReport
|
class ProductReport extends AbstractReport
|
||||||
{
|
{
|
||||||
@ -48,8 +49,8 @@ class ProductReport extends AbstractReport
|
|||||||
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
|
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||||
$invoice->present()->invoice_date,
|
$invoice->present()->invoice_date,
|
||||||
$item->product_key,
|
$item->product_key,
|
||||||
$item->qty,
|
Utils::roundSignificant($item->qty, 0),
|
||||||
$account->formatMoney($item->cost, $client),
|
Utils::roundSignificant($item->cost, 2),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,8 +526,8 @@ class InvoiceRepository extends BaseRepository
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoiceItemCost = round(Utils::parseFloat($item['cost']), 2);
|
$invoiceItemCost = Utils::roundSignificant(Utils::parseFloat($item['cost']));
|
||||||
$invoiceItemQty = round(Utils::parseFloat($item['qty']), 2);
|
$invoiceItemQty = Utils::roundSignificant(Utils::parseFloat($item['qty']));
|
||||||
|
|
||||||
$lineTotal = $invoiceItemCost * $invoiceItemQty;
|
$lineTotal = $invoiceItemCost * $invoiceItemQty;
|
||||||
$total += round($lineTotal, 2);
|
$total += round($lineTotal, 2);
|
||||||
@ -535,8 +535,8 @@ class InvoiceRepository extends BaseRepository
|
|||||||
|
|
||||||
foreach ($data['invoice_items'] as $item) {
|
foreach ($data['invoice_items'] as $item) {
|
||||||
$item = (array) $item;
|
$item = (array) $item;
|
||||||
$invoiceItemCost = round(Utils::parseFloat($item['cost']), 2);
|
$invoiceItemCost = Utils::roundSignificant(Utils::parseFloat($item['cost']));
|
||||||
$invoiceItemQty = round(Utils::parseFloat($item['qty']), 2);
|
$invoiceItemQty = Utils::roundSignificant(Utils::parseFloat($item['qty']));
|
||||||
$lineTotal = $invoiceItemCost * $invoiceItemQty;
|
$lineTotal = $invoiceItemCost * $invoiceItemQty;
|
||||||
|
|
||||||
if ($invoice->discount > 0) {
|
if ($invoice->discount > 0) {
|
||||||
|
35
database/migrations/2017_08_14_085334_increase_precision.php
Normal file
35
database/migrations/2017_08_14_085334_increase_precision.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class IncreasePrecision extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('products', function ($table) {
|
||||||
|
$table->decimal('cost', 15, 4)->change();
|
||||||
|
$table->decimal('qty', 15, 4)->change();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('invoice_items', function ($table) {
|
||||||
|
$table->decimal('cost', 15, 4)->change();
|
||||||
|
$table->decimal('qty', 15, 4)->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -494,7 +494,7 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
|||||||
|
|
||||||
var row = [];
|
var row = [];
|
||||||
var item = invoice.invoice_items[i];
|
var item = invoice.invoice_items[i];
|
||||||
var cost = formatMoneyInvoice(item.cost, invoice, 'none');
|
var cost = formatMoneyInvoice(item.cost, invoice, 'none', getPrecision(item.cost));
|
||||||
var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : '';
|
var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : '';
|
||||||
var notes = item.notes;
|
var notes = item.notes;
|
||||||
var productKey = item.product_key;
|
var productKey = item.product_key;
|
||||||
@ -537,7 +537,7 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
|||||||
custom_value2 = processVariables(item.custom_value2);
|
custom_value2 = processVariables(item.custom_value2);
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
var lineTotal = roundSignificant(NINJA.parseFloat(item.cost)) * roundSignificant(NINJA.parseFloat(item.qty));
|
||||||
if (account.include_item_taxes_inline == '1') {
|
if (account.include_item_taxes_inline == '1') {
|
||||||
var taxAmount1 = 0;
|
var taxAmount1 = 0;
|
||||||
var taxAmount2 = 0;
|
var taxAmount2 = 0;
|
||||||
|
@ -666,7 +666,7 @@ function calculateAmounts(invoice) {
|
|||||||
// sum line item
|
// sum line item
|
||||||
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 lineTotal = invoice.is_statement ? roundToTwo(NINJA.parseFloat(item.balance)) : roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
var lineTotal = invoice.is_statement ? roundToTwo(NINJA.parseFloat(item.balance)) : roundSignificant(NINJA.parseFloat(item.cost)) * roundSignificant(NINJA.parseFloat(item.qty));
|
||||||
lineTotal = roundToTwo(lineTotal);
|
lineTotal = roundToTwo(lineTotal);
|
||||||
if (lineTotal) {
|
if (lineTotal) {
|
||||||
total += lineTotal;
|
total += lineTotal;
|
||||||
@ -1042,6 +1042,22 @@ function toggleDatePicker(field) {
|
|||||||
$('#'+field).datepicker('show');
|
$('#'+field).datepicker('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPrecision(number) {
|
||||||
|
if (roundToPrecision(number, 3) != number) {
|
||||||
|
return 4;
|
||||||
|
} else if (roundToPrecision(number, 2) != number) {
|
||||||
|
return 3;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function roundSignificant(number) {
|
||||||
|
var precision = getPrecision(number);
|
||||||
|
var value = roundToPrecision(number, precision);
|
||||||
|
return isNaN(value) ? 0 : value;
|
||||||
|
}
|
||||||
|
|
||||||
function roundToTwo(number, toString) {
|
function roundToTwo(number, toString) {
|
||||||
var val = roundToPrecision(number, 2);
|
var val = roundToPrecision(number, 2);
|
||||||
return toString ? val.toFixed(2) : (val || 0);
|
return toString ? val.toFixed(2) : (val || 0);
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
@if ($product)
|
@if ($product)
|
||||||
{{ Former::populate($product) }}
|
{{ Former::populate($product) }}
|
||||||
{{ Former::populateField('cost', number_format($product->cost, 2, '.', '')) }}
|
{{ Former::populateField('cost', Utils::roundSignificant($product->cost)) }}
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{!! Former::text('product_key')->label('texts.product') !!}
|
{!! Former::text('product_key')->label('texts.product') !!}
|
||||||
|
@ -811,7 +811,7 @@ function ItemModel(data) {
|
|||||||
|
|
||||||
this.prettyQty = ko.computed({
|
this.prettyQty = ko.computed({
|
||||||
read: function () {
|
read: function () {
|
||||||
return NINJA.parseFloat(this.qty()) ? NINJA.parseFloat(this.qty()) : '';
|
return NINJA.parseFloat(this.qty()) ? roundSignificant(NINJA.parseFloat(this.qty())) : '';
|
||||||
},
|
},
|
||||||
write: function (value) {
|
write: function (value) {
|
||||||
this.qty(value);
|
this.qty(value);
|
||||||
@ -821,7 +821,7 @@ function ItemModel(data) {
|
|||||||
|
|
||||||
this.prettyCost = ko.computed({
|
this.prettyCost = ko.computed({
|
||||||
read: function () {
|
read: function () {
|
||||||
return this.cost() ? this.cost() : '';
|
return this.cost() ? roundSignificant(this.cost()) : '';
|
||||||
},
|
},
|
||||||
write: function (value) {
|
write: function (value) {
|
||||||
this.cost(value);
|
this.cost(value);
|
||||||
@ -836,8 +836,8 @@ function ItemModel(data) {
|
|||||||
this.totals = ko.observable();
|
this.totals = ko.observable();
|
||||||
|
|
||||||
this.totals.rawTotal = ko.computed(function() {
|
this.totals.rawTotal = ko.computed(function() {
|
||||||
var cost = roundToTwo(NINJA.parseFloat(self.cost()));
|
var cost = roundSignificant(NINJA.parseFloat(self.cost()));
|
||||||
var qty = roundToTwo(NINJA.parseFloat(self.qty()));
|
var qty = roundSignificant(NINJA.parseFloat(self.qty()));
|
||||||
var value = cost * qty;
|
var value = cost * qty;
|
||||||
return value ? roundToTwo(value) : 0;
|
return value ? roundToTwo(value) : 0;
|
||||||
});
|
});
|
||||||
@ -979,7 +979,7 @@ ko.bindingHandlers.productTypeahead = {
|
|||||||
model.notes(datum.notes);
|
model.notes(datum.notes);
|
||||||
}
|
}
|
||||||
if (datum.cost) {
|
if (datum.cost) {
|
||||||
model.cost(accounting.toFixed(datum.cost, 2));
|
model.cost(roundSignificant(datum.cost, 2));
|
||||||
}
|
}
|
||||||
if (!model.qty()) {
|
if (!model.qty()) {
|
||||||
model.qty(1);
|
model.qty(1);
|
||||||
|
@ -33,14 +33,14 @@
|
|||||||
return window.parseFloat(str);
|
return window.parseFloat(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMoneyInvoice(value, invoice, decorator) {
|
function formatMoneyInvoice(value, invoice, decorator, precision) {
|
||||||
var account = invoice.account;
|
var account = invoice.account;
|
||||||
var client = invoice.client;
|
var client = invoice.client;
|
||||||
|
|
||||||
return formatMoneyAccount(value, account, client, decorator);
|
return formatMoneyAccount(value, account, client, decorator, precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMoneyAccount(value, account, client, decorator) {
|
function formatMoneyAccount(value, account, client, decorator, precision) {
|
||||||
var currencyId = false;
|
var currencyId = false;
|
||||||
var countryId = false;
|
var countryId = false;
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
decorator = parseInt(account.show_currency_code) ? 'code' : 'symbol';
|
decorator = parseInt(account.show_currency_code) ? 'code' : 'symbol';
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatMoney(value, currencyId, countryId, decorator)
|
return formatMoney(value, currencyId, countryId, decorator, precision)
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatAmount(value, currencyId) {
|
function formatAmount(value, currencyId) {
|
||||||
@ -81,19 +81,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMoney(value, currencyId, countryId, decorator) {
|
function formatMoney(value, currencyId, countryId, decorator, precision) {
|
||||||
value = NINJA.parseFloat(value);
|
value = NINJA.parseFloat(value);
|
||||||
|
|
||||||
if (!currencyId) {
|
if (!currencyId) {
|
||||||
currencyId = {{ Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY) }};
|
currencyId = {{ Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY) }};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var currency = currencyMap[currencyId];
|
||||||
|
|
||||||
if (!decorator) {
|
if (!decorator) {
|
||||||
decorator = '{{ Session::get(SESSION_CURRENCY_DECORATOR, CURRENCY_DECORATOR_SYMBOL) }}';
|
decorator = '{{ Session::get(SESSION_CURRENCY_DECORATOR, CURRENCY_DECORATOR_SYMBOL) }}';
|
||||||
}
|
}
|
||||||
|
|
||||||
var currency = currencyMap[currencyId];
|
if (!precision) {
|
||||||
var precision = currency.precision;
|
precision = currency.precision;
|
||||||
|
} else if (currency.precision == 0) {
|
||||||
|
precision = 0;
|
||||||
|
}
|
||||||
|
|
||||||
var thousand = currency.thousand_separator;
|
var thousand = currency.thousand_separator;
|
||||||
var decimal = currency.decimal_separator;
|
var decimal = currency.decimal_separator;
|
||||||
var code = currency.code;
|
var code = currency.code;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user