Support higher precision for unit cost and quantity #252

This commit is contained in:
Hillel Coren 2017-08-14 14:06:50 +03:00
parent 4e285c8ac6
commit 7a4a5d51f3
11 changed files with 117 additions and 47 deletions

View File

@ -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;

View File

@ -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),
]; ];
} }

View File

@ -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) {

View 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

View File

@ -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;

View File

@ -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);

View File

@ -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') !!}

View File

@ -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);

View File

@ -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;