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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return strlen($string) > $length ? rtrim(substr($string, 0, $length)) . '...' : $string;
|
||||
|
@ -4,6 +4,7 @@ namespace App\Ninja\Reports;
|
||||
|
||||
use App\Models\Client;
|
||||
use Auth;
|
||||
use Utils;
|
||||
|
||||
class ProductReport extends AbstractReport
|
||||
{
|
||||
@ -48,8 +49,8 @@ class ProductReport extends AbstractReport
|
||||
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||
$invoice->present()->invoice_date,
|
||||
$item->product_key,
|
||||
$item->qty,
|
||||
$account->formatMoney($item->cost, $client),
|
||||
Utils::roundSignificant($item->qty, 0),
|
||||
Utils::roundSignificant($item->cost, 2),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -526,8 +526,8 @@ class InvoiceRepository extends BaseRepository
|
||||
continue;
|
||||
}
|
||||
|
||||
$invoiceItemCost = round(Utils::parseFloat($item['cost']), 2);
|
||||
$invoiceItemQty = round(Utils::parseFloat($item['qty']), 2);
|
||||
$invoiceItemCost = Utils::roundSignificant(Utils::parseFloat($item['cost']));
|
||||
$invoiceItemQty = Utils::roundSignificant(Utils::parseFloat($item['qty']));
|
||||
|
||||
$lineTotal = $invoiceItemCost * $invoiceItemQty;
|
||||
$total += round($lineTotal, 2);
|
||||
@ -535,8 +535,8 @@ class InvoiceRepository extends BaseRepository
|
||||
|
||||
foreach ($data['invoice_items'] as $item) {
|
||||
$item = (array) $item;
|
||||
$invoiceItemCost = round(Utils::parseFloat($item['cost']), 2);
|
||||
$invoiceItemQty = round(Utils::parseFloat($item['qty']), 2);
|
||||
$invoiceItemCost = Utils::roundSignificant(Utils::parseFloat($item['cost']));
|
||||
$invoiceItemQty = Utils::roundSignificant(Utils::parseFloat($item['qty']));
|
||||
$lineTotal = $invoiceItemCost * $invoiceItemQty;
|
||||
|
||||
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 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 notes = item.notes;
|
||||
var productKey = item.product_key;
|
||||
@ -537,7 +537,7 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
||||
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') {
|
||||
var taxAmount1 = 0;
|
||||
var taxAmount2 = 0;
|
||||
|
@ -666,7 +666,7 @@ function calculateAmounts(invoice) {
|
||||
// sum line item
|
||||
for (var i=0; i<invoice.invoice_items.length; 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);
|
||||
if (lineTotal) {
|
||||
total += lineTotal;
|
||||
@ -1042,6 +1042,22 @@ function toggleDatePicker(field) {
|
||||
$('#'+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) {
|
||||
var val = roundToPrecision(number, 2);
|
||||
return toString ? val.toFixed(2) : (val || 0);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
@if ($product)
|
||||
{{ Former::populate($product) }}
|
||||
{{ Former::populateField('cost', number_format($product->cost, 2, '.', '')) }}
|
||||
{{ Former::populateField('cost', Utils::roundSignificant($product->cost)) }}
|
||||
@endif
|
||||
|
||||
{!! Former::text('product_key')->label('texts.product') !!}
|
||||
|
@ -811,7 +811,7 @@ function ItemModel(data) {
|
||||
|
||||
this.prettyQty = ko.computed({
|
||||
read: function () {
|
||||
return NINJA.parseFloat(this.qty()) ? NINJA.parseFloat(this.qty()) : '';
|
||||
return NINJA.parseFloat(this.qty()) ? roundSignificant(NINJA.parseFloat(this.qty())) : '';
|
||||
},
|
||||
write: function (value) {
|
||||
this.qty(value);
|
||||
@ -821,7 +821,7 @@ function ItemModel(data) {
|
||||
|
||||
this.prettyCost = ko.computed({
|
||||
read: function () {
|
||||
return this.cost() ? this.cost() : '';
|
||||
return this.cost() ? roundSignificant(this.cost()) : '';
|
||||
},
|
||||
write: function (value) {
|
||||
this.cost(value);
|
||||
@ -836,8 +836,8 @@ function ItemModel(data) {
|
||||
this.totals = ko.observable();
|
||||
|
||||
this.totals.rawTotal = ko.computed(function() {
|
||||
var cost = roundToTwo(NINJA.parseFloat(self.cost()));
|
||||
var qty = roundToTwo(NINJA.parseFloat(self.qty()));
|
||||
var cost = roundSignificant(NINJA.parseFloat(self.cost()));
|
||||
var qty = roundSignificant(NINJA.parseFloat(self.qty()));
|
||||
var value = cost * qty;
|
||||
return value ? roundToTwo(value) : 0;
|
||||
});
|
||||
@ -979,7 +979,7 @@ ko.bindingHandlers.productTypeahead = {
|
||||
model.notes(datum.notes);
|
||||
}
|
||||
if (datum.cost) {
|
||||
model.cost(accounting.toFixed(datum.cost, 2));
|
||||
model.cost(roundSignificant(datum.cost, 2));
|
||||
}
|
||||
if (!model.qty()) {
|
||||
model.qty(1);
|
||||
|
@ -33,14 +33,14 @@
|
||||
return window.parseFloat(str);
|
||||
}
|
||||
|
||||
function formatMoneyInvoice(value, invoice, decorator) {
|
||||
function formatMoneyInvoice(value, invoice, decorator, precision) {
|
||||
var account = invoice.account;
|
||||
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 countryId = false;
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
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) {
|
||||
@ -81,19 +81,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
function formatMoney(value, currencyId, countryId, decorator) {
|
||||
function formatMoney(value, currencyId, countryId, decorator, precision) {
|
||||
value = NINJA.parseFloat(value);
|
||||
|
||||
if (!currencyId) {
|
||||
currencyId = {{ Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY) }};
|
||||
}
|
||||
|
||||
var currency = currencyMap[currencyId];
|
||||
|
||||
if (!decorator) {
|
||||
decorator = '{{ Session::get(SESSION_CURRENCY_DECORATOR, CURRENCY_DECORATOR_SYMBOL) }}';
|
||||
}
|
||||
|
||||
var currency = currencyMap[currencyId];
|
||||
var precision = currency.precision;
|
||||
if (!precision) {
|
||||
precision = currency.precision;
|
||||
} else if (currency.precision == 0) {
|
||||
precision = 0;
|
||||
}
|
||||
|
||||
var thousand = currency.thousand_separator;
|
||||
var decimal = currency.decimal_separator;
|
||||
var code = currency.code;
|
||||
|
Loading…
x
Reference in New Issue
Block a user